package com.dd.cloud.user.service.doctor.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dd.cloud.common.config.common.ProfilesProperties;
import com.dd.cloud.common.config.servlet.JwtUtils;
import com.dd.cloud.common.config.sms.SmsComponent;
import com.dd.cloud.common.constants.*;
import com.dd.cloud.common.enums.ZkExceptionEnum;
import com.dd.cloud.common.exception.ZekeException;
import com.dd.cloud.common.interceptor.UserInterceptor;
import com.dd.cloud.common.utils.*;
import com.dd.cloud.common.vo.LoginInfo;
import com.dd.cloud.common.vo.PageVo;
import com.dd.cloud.user.config.WechatConfig;
import com.dd.cloud.user.config.WechatPayConfig;
import com.dd.cloud.user.entity.admin.AdminSaleInfo;
import com.dd.cloud.user.entity.admin.SaleInviteDoctor;
import com.dd.cloud.user.entity.agent.Agent;
import com.dd.cloud.user.entity.agent.AgentDoctorBind;
import com.dd.cloud.user.entity.consult.WebSocketPrivateGroup;
import com.dd.cloud.user.entity.doctor.*;
import com.dd.cloud.user.entity.mechan.MechanRank;
import com.dd.cloud.user.entity.mechan.MechanUser;
import com.dd.cloud.user.entity.menu.ClientMenu;
import com.dd.cloud.user.entity.product.ProductGiveRecord;
import com.dd.cloud.user.entity.product.ProductGiveRule;
import com.dd.cloud.user.mapper.doctor.DoctorUserMapper;
import com.dd.cloud.user.mq.MQConfig;
import com.dd.cloud.user.mq.RabbitSend;
import com.dd.cloud.user.req.doctor.*;
import com.dd.cloud.user.req.plat.PlatBindAccountReq;
import com.dd.cloud.user.res.admin.StatisticalUserNumRes;
import com.dd.cloud.user.res.doctor.*;
import com.dd.cloud.user.service.admin.IAdminSaleInfoService;
import com.dd.cloud.user.service.admin.ISaleInviteDoctorService;
import com.dd.cloud.user.service.agent.IAgentDoctorBindService;
import com.dd.cloud.user.service.agent.IAgentService;
import com.dd.cloud.user.service.common.ICommonService;
import com.dd.cloud.user.service.consule.IWebSocketPrivateGroupService;
import com.dd.cloud.user.service.doctor.*;
import com.dd.cloud.user.service.mechan.IMechanRankService;
import com.dd.cloud.user.service.mechan.IMechanUserService;
import com.dd.cloud.user.service.menu.IClientMenuUserService;
import com.dd.cloud.user.service.news.INewsInfoService;
import com.dd.cloud.user.service.plat.IPlatformLoginService;
import com.dd.cloud.user.service.plat.ITrustDeviceService;
import com.dd.cloud.user.service.product.IProductGiveRecordService;
import com.dd.cloud.user.service.product.IProductGiveRuleService;
import com.dd.cloud.user.vo.AutoLoginModel;
import com.dd.cloud.user.vo.DelayMqVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 医生账号信息表  服务实现类
 * </p>
 *
 * @author czg
 * @since 2022-04-27
 */
@Service
@Slf4j
public class DoctorUserServiceImpl extends ServiceImpl<DoctorUserMapper, DoctorUser> implements IDoctorUserService {
    @Autowired
    private JwtUtils jwtUtils;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private IDoctorDetailService doctorDetailService;
    @Autowired
    private IClientMenuUserService clientMenuUserService;
    @Autowired
    private IMechanRankService mechanRankService;
    @Autowired
    private SmsComponent smsComponent;
    @Autowired
    private IMechanUserService mechanUserService;
    @Autowired
    private IPlatformLoginService platformLoginService;
    @Autowired
    private ITrustDeviceService trustDeviceService;
    @Autowired
    private WechatConfig wechatConfig;
    @Autowired
    private WechatPayConfig wechatPayConfig;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private ProfilesProperties profilesProperties;
    @Autowired
    private ISaleInviteDoctorService saleInviteDoctorService;
    @Autowired
    private IAgentDoctorBindService agentDoctorBindService;
    @Autowired
    private IBindReviewPresDoctorService bindReviewPresDoctorService;
    @Value("${verify}")
    private Boolean verify;
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;
    @Autowired
    private TransactionDefinition transactionDefinition;
    @Autowired
    private INewsInfoService newsInfoService;

    @Autowired
    private IAdminSaleInfoService adminSaleInfoService;

    @Autowired
    private IAgentService agentService;

    @Autowired
    private IDoctorDetailApplyService doctorDetailApplyService;

    @Autowired
    private RabbitSend rabbitSend;
    @Autowired
    private ICommonService commonService;

    @Autowired
    private IDoctorIntegralService doctorIntegralService;

    @Autowired
    private IProductGiveRuleService productGiveRuleService;


    @Autowired
    private IProductGiveRecordService productGiveRecordService;

    /**
     * 医生通过手机验证码登录
     *
     * @param phone 手机
     * @param code  验证码
     * @return 医生账号信息
     */
    @Override
    public DoctorUser doctorLoginByPhone(HttpServletRequest request, HttpServletResponse response, String phone, String code, String model, String platId, String appId, Integer type, String cid) {
        String client = request.getHeader("client");
        log.info("客户端为:{}", client);
        if (StringUtils.isBlank(model) && StringUtils.isBlank(code)) {
            throw new ZekeException(ZkExceptionEnum.INVALID_VERIFY_CODE);
        }
        if (!StringUtils.isBlank(code)) {
            smsComponent.verifyPhoneCode(phone, code);
        }
        QueryWrapper<DoctorUser> q = new QueryWrapper<>();
        q.lambda().eq(DoctorUser::getMobileNumber, phone);
        q.lambda().notIn(DoctorUser::getStatus, UserConstants.DoctorUserStatus.DEL, UserConstants.DoctorUserStatus.LOGOFF);
        DoctorUser doc = getOne(q);
        LoginInfo loginInfo;
        if (doc != null) {
            if (StringUtils.isBlank(code)) {
                verifyAppLogin(request, response, phone, model, doc.getPassword());
            }
            if (doc.getIsCertification() == UserConstants.IsCertification.certification) {
                MechanRank rank = mechanRankService.getById(doc.getMechanId());
                if (rank == null || rank.getStatus() == MechanConstants.MechanStatus.DEL) {
                    throw new ZekeException(400, "机构不存在");
                }
                if (rank.getStatus() == MechanConstants.MechanStatus.DISABLEADMIN) {
                    throw new ZekeException(400, "机构已被禁用");
                }
            }
            switchStatus(doc);
            QueryWrapper<DoctorDetail> qw = new QueryWrapper<>();
            qw.lambda().eq(DoctorDetail::getDoctorId, doc.getId()).eq(DoctorDetail::getStatus, UserConstants.DoctorDetailStatus.NORMAL);
            DoctorDetail doctorDetail = doctorDetailService.getOne(qw);
            //生成token并写入cookie
            loginInfo = new LoginInfo(doc.getId(), doc.getMechanId(), LoginInfo.UserType.DOCTOR, doctorDetail.getName(), jwtUtils.getUserAgent(request));
            jwtUtils.createToken(loginInfo, request, response);
        } else {
            //PC端登录不注册 找会密码注册
            if (HealthConstants.ClientType.HEALTHPC.equals(client)) {
//                if (false) {
                throw new ZekeException(412, "账号不存在，请前往大道APP注册");
//                }
            }
            if (StringUtils.isBlank(code)) {
                throw new ZekeException(ZkExceptionEnum.INVALID_VERIFY_CODE);
            }
            doc = new DoctorUser();
            doc.setType(1);
            doc.setMobileNumber(phone);
//            doc.setStatus(UserConstants.DoctorUserStatus.TRIAL);
            doc.setIsCertification(UserConstants.IsCertification.not_certification);
            doc.setIsPresPower(0);
            doc.setStatus(UserConstants.DoctorUserStatus.NORMAL);
            doc.setCreateDate(LocalDateTime.now());
            doc.setMechanId(0);
            if (HealthConstants.ClientType.HEALTHPC.equals(client)) {
                doc.setCreateAccountType(2);
            } else {
                doc.setCreateAccountType(1);
            }
            save(doc);
            DoctorUser update = new DoctorUser();
            update.setId(doc.getId());
            update.setInviteCode(HealthConstants.InviteCodePrefixConstants.DOCTOR_PREFIX + SerialNumberUtils.generatorInviteCode(doc.getId(), 5));
            updateById(update);
            DoctorDetail dd = new DoctorDetail();
            dd.setDoctorId(doc.getId());
            dd.setMobileNumber(phone);
            dd.setName(phone);
            dd.setStatus(UserConstants.DoctorDetailStatus.NORMAL);
            dd.setCreateDate(LocalDateTime.now());
            doctorDetailService.save(dd);
            //生成token并写入cookie
            loginInfo = new LoginInfo(doc.getId(), doc.getMechanId(), LoginInfo.UserType.DOCTOR, dd.getName(), jwtUtils.getUserAgent(request));
            jwtUtils.createToken(loginInfo, request, response);
        }
        if (!StringUtils.isBlank(model)) {
            setAppLogin(request, phone, model, doc.getPassword());
        }
        if (!StringUtils.isBlank(appId) && type != null && !StringUtils.isBlank(platId)) {
            PlatBindAccountReq req = new PlatBindAccountReq();
            req.setAppId(appId);
            req.setPlatId(platId);
            req.setType(type);
            req.setUserId(doc.getId());
            req.setUserType(LoginInfo.UserType.DOCTOR);
            Integer i = platformLoginService.bindAccountPlat(response, req);
            doc.setBind(i);
        }
        List<ClientMenu> list = clientMenuUserService.getClientMenuUserList(loginInfo, request.getHeader("client"));
        doc.setMenuList(list);
        if (doc.getPassword() != null) {
            doc.setPassword("1");
        }
        doc.setSalt(null);
        if (doc.getIsPresPower() != null && doc.getIsPresPower() == 0) {
            doc.setReviewPresDoctorCount(bindReviewPresDoctorService.queryBindReviewPresDoctorNormalCount(doc.getId()));
        }
        if (!HealthConstants.ClientType.HEALTHPC.equals(client)) {
            //信任当前设备/刷新设备最后一次登录时间
            trustDeviceService.trustDevice(doc.getId(), LoginInfo.UserType.DOCTOR, model, cid);
        }
        if (doc.getId() != null) {
            //登录发送积分
            String params = IntegralConstants.IntegralType.SIXTEEN_TYPE + "-" + doc.getId();
            log.info("连续登录,参数:{}", params);
            rabbitSend.sendMessage(MQConfig.INTEGRAL_EXCHANGE, MQConfig.INTEGRAL_QUEUE, params);
        }
        //记录首次登录时间
//        if (doc.getType() == 2) {
//            saveLoginTime(doc.getId());
//        }
        return doc;
    }

    @Autowired
    private IDoctorEquityService doctorEquityService;

    private void saveLoginTime(Integer doctorId) {
        LambdaQueryWrapper<DoctorEquity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DoctorEquity::getDoctorId, doctorId)
                .eq(DoctorEquity::getType, 2);
        DoctorEquity doctorEquity = doctorEquityService.getOne(queryWrapper);
        if (doctorEquity != null && doctorEquity.getStartDate() != null) {
            doctorEquity.setStartDate(LocalDateTime.now());
            doctorEquityService.updateById(doctorEquity);
        }
    }

    /**
     * 修改医生状态
     *
     * @param doctorId 医生id
     * @param status   状态
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDoctorUserStatusById(Integer doctorId, Integer status) {
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo().get();
        DoctorUser du = getById(doctorId);
        if (du == null) {
            throw new ZekeException(ZkExceptionEnum.ACCOUNT_NOT_FOUND);
        }
        if (loginInfo.getType() == LoginInfo.UserType.DOCTOR) {
            throw new ZekeException(ZkExceptionEnum.NO_ERMSSION);
        }
        if (loginInfo.getType() == LoginInfo.UserType.MECHAN) {
            if (status.equals(UserConstants.DoctorUserStatus.NORMAL)) {
                if (du.getStatus().equals(UserConstants.DoctorUserStatus.DISABLEADMIN)) {
                    throw new ZekeException(400, "运营后台停用的账号,无权限启用");
                }
            }
            if (!UserConstants.DoctorUserStatus.mechanOper.contains(status)) {
                throw new ZekeException(ZkExceptionEnum.STATUS_DATA_ERROR);
            }
        }
        if (loginInfo.getType() == LoginInfo.UserType.ADMIN) {
            if (!ObjectUtils.refrect(UserConstants.DoctorUserStatus.class, status)) {
                throw new ZekeException(ZkExceptionEnum.STATUS_DATA_ERROR);
            }
        }
        //删除医生, 删除医生详情
        if (status.equals(UserConstants.DoctorUserStatus.DEL) || status == UserConstants.DoctorUserStatus.DISABLEMECHAN || status == UserConstants.DoctorUserStatus.DISABLEADMIN || status == UserConstants.DoctorUserStatus.LOGOFF) {
            if (status.equals(UserConstants.DoctorUserStatus.DEL) || status.equals(UserConstants.DoctorUserStatus.LOGOFF)) {
                DoctorDetail dd = new DoctorDetail();
                dd.setStatus(status);
                UpdateWrapper<DoctorDetail> uw = new UpdateWrapper<>();
                uw.lambda().eq(DoctorDetail::getDoctorId, doctorId);
                boolean i = doctorDetailService.update(dd, uw);
                if (!i) {
                    throw new ZekeException(ZkExceptionEnum.UPDATE_ERROR);
                }
                String logoffName = loginInfo.getName();
                du.setLogoffName(logoffName);
                du.setLogoffDate(LocalDateTime.now());
            }
            redisTemplate.delete(HealthConstants.ClientType.HEALTHMOB + du.getMechanId() + "-" + doctorId + "-" + LoginInfo.UserType.DOCTOR);
            redisTemplate.delete(HealthConstants.ClientType.HEALTHPC + du.getMechanId() + "-" + doctorId + "-" + LoginInfo.UserType.DOCTOR);
        }
        du.setId(doctorId);
        du.setStatus(status);
        boolean flag = updateById(du);
        if (!flag) {
            throw new ZekeException(ZkExceptionEnum.UPDATE_ERROR);
        }
    }

    /**
     * 医生通过账号密码登录
     *
     * @param phone 手机
     * @param psw   密码
     * @return 医生账号信息
     */
    @Override
    public DoctorUser doctorLoginByAccount(HttpServletRequest request, HttpServletResponse response, String phone, String psw, String model, String platId, String appId, Integer type, String cid) {
        //检查密码错误是否超过次数
        commonService.checkLoginPwdError(request, phone);
        String client = request.getHeader("client");
        if (StringUtils.isBlank(model) && StringUtils.isBlank(psw)) {
            throw new ZekeException("密码不能为空");
        }
        //查询账号
        QueryWrapper<DoctorUser> q = new QueryWrapper<>();
        q.lambda().eq(DoctorUser::getMobileNumber, phone);
        q.lambda().notIn(DoctorUser::getStatus, UserConstants.DoctorUserStatus.DEL, UserConstants.DoctorUserStatus.LOGOFF);
        DoctorUser doc = getOne(q);
        if (doc == null) {
            throw new ZekeException(ZkExceptionEnum.INVALID_USERNAME_PASSWORD);
        }
        if (!StringUtils.isBlank(psw)) {
            if (doc.getPassword() == null) {
                throw new ZekeException(406, "账号未设置密码");
            }
            //密码校验
            q.lambda().eq(DoctorUser::getPassword, CodecUtils.md5Hex(psw, doc.getSalt()));
            doc = getOne(q);
            if (doc == null) {
                //密码错误增加次数
                commonService.loginPwdError(request, phone);
                throw new ZekeException(ZkExceptionEnum.INVALID_USERNAME_PASSWORD);
            }
        } else {
            verifyAppLogin(request, response, phone, model, doc.getPassword());
        }
        if (doc.getIsCertification() == UserConstants.IsCertification.certification) {
            MechanRank rank = mechanRankService.getById(doc.getMechanId());
            if (rank == null || rank.getStatus() == MechanConstants.MechanStatus.DEL) {
                throw new ZekeException(400, "机构不存在");
            }
            if (rank.getStatus() == MechanConstants.MechanStatus.DISABLEADMIN) {
                throw new ZekeException(400, "机构已被禁用");
            }
        }
        switchStatus(doc);
        QueryWrapper<DoctorDetail> qw = new QueryWrapper<>();
        qw.lambda().eq(DoctorDetail::getDoctorId, doc.getId()).eq(DoctorDetail::getStatus, UserConstants.DoctorDetailStatus.NORMAL);
        DoctorDetail doctorDetail = doctorDetailService.getOne(qw);
        if (doctorDetail == null) {
            throw new ZekeException(400, "账号无详情, 请联系管理员");
        }
        if (!HealthConstants.ClientType.HEALTHPC.equals(client)) {
            boolean flag = trustDeviceService.verifyDevice(phone, LoginInfo.UserType.DOCTOR, model);
            if (!flag && verify) {
                throw new ZekeException(405, "账号在新设备登录, 请使用手机验证码登录");
            }
        }
        //生成token并写入cookie
        LoginInfo loginInfo = new LoginInfo(doc.getId(), doc.getMechanId(), LoginInfo.UserType.DOCTOR, doctorDetail.getName(), jwtUtils.getUserAgent(request));
        jwtUtils.createToken(loginInfo, request, response);
        if (!StringUtils.isBlank(model)) {
            setAppLogin(request, phone, model, doc.getPassword());
        }
        List<ClientMenu> list = clientMenuUserService.getClientMenuUserList(loginInfo, request.getHeader("client"));
        doc.setMenuList(list);
        doc.setPassword("1");
        doc.setSalt(null);
        if (doc.getIsPresPower() == 0) {
            doc.setReviewPresDoctorCount(bindReviewPresDoctorService.queryBindReviewPresDoctorNormalCount(doc.getId()));
        }
        if (!HealthConstants.ClientType.HEALTHPC.equals(client)) {
            //刷新设备最后一次登录时间
            trustDeviceService.trustDevice(doc.getId(), LoginInfo.UserType.DOCTOR, model, cid);
        }
        if (doc.getId() != null) {
            //登录发送积分
            String params = IntegralConstants.IntegralType.SIXTEEN_TYPE + "-" + doc.getId();
            log.info("连续登录,参数:{}", params);
            rabbitSend.sendMessage(MQConfig.INTEGRAL_EXCHANGE, MQConfig.INTEGRAL_QUEUE, params);
        }
//        if (doc.getType() == 2) {
//            saveLoginTime(doc.getId());
//        }
        return doc;
    }

    public void verifyAppLogin(HttpServletRequest request, HttpServletResponse response, String phone, String model, String pwd) {
        String userAgent = jwtUtils.getUserAgent(request);
        String val = getAppLogin(phone, model);
        log.info("查询自动登录信息:{}", val);
        if (val == null) {
            response.setStatus(403);
            throw new ZekeException(403, "登录信息失效");
        }
        AutoLoginModel autoLoginModel = JsonUtils.strToObject(val, AutoLoginModel.class);
        if (autoLoginModel == null) {
            response.setStatus(403);
            throw new ZekeException(403, "登录信息失效");
        }
        //登录标识不一致, 不能自动登录
        if (!userAgent.equals(autoLoginModel.getUserAgent())) {
            response.setStatus(403);
            throw new ZekeException(403, "登录信息失效");
        }
        //String[] str = val.split("-");
        //log.info("拆解信息:{}", (Object) str);
        long timestamp1 = autoLoginModel.getTimestamp();
        long timestamp2 = DateUtils.toTimestamp(LocalDateTime.now());
        //上次登录时间加上7天
        timestamp1 += 1000 * 60 * HealthConstants.APP_LOGIN_EXPIRE;
        if (timestamp2 > timestamp1) {
            response.setStatus(403);
            throw new ZekeException(403, "登录信息失效");
        }
        //String password = str.length > 1 ? str[1] : "";
        if (autoLoginModel.getPwd() == null) {
            autoLoginModel.setPwd("0");
        }
        if (!autoLoginModel.getPwd().equals(pwd)) {
            response.setStatus(403);
            throw new ZekeException(403, "登录信息失效");
        }
    }

    /**
     * @param request:
     * @param phone:
     * @param model:
     * @param pwd:
     * @return void
     * @description 设置自动登录信息
     * @author czg
     * @date 2023/12/15 11:13
     */
    public void setAppLogin(HttpServletRequest request, String phone, String model, String pwd) {
        if (pwd == null) {
            pwd = "0";
        }

        AutoLoginModel autoLoginModel = new AutoLoginModel();
        autoLoginModel.setTimestamp(DateUtils.toTimestamp(LocalDateTime.now()));
        autoLoginModel.setPwd(pwd);
        //设置客户端标识,
        autoLoginModel.setUserAgent(jwtUtils.getUserAgent(request));
        redisTemplate.opsForValue().set(phone + "-" + model, JsonUtils.objectToStr(autoLoginModel), HealthConstants.APP_LOGIN_EXPIRE, TimeUnit.MINUTES);
    }

    public String getAppLogin(String phone, String model) {
        return redisTemplate.opsForValue().get(phone + "-" + model);
    }

    /**
     * 医生忘记密码
     *
     * @param phone
     * @param code
     * @param pwd
     * @param pwds
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void doctorForgetPwd(String phone, String code, String pwd, String pwds) {
        if (!pwd.equals(pwds)) {
            throw new ZekeException(400, "两次密码不一致");
        }
        if (StringUtils.isBlank(code)) {
            throw new ZekeException(ZkExceptionEnum.INVALID_VERIFY_CODE);
        }
        QueryWrapper<DoctorUser> q = new QueryWrapper<>();
        q.lambda().eq(DoctorUser::getMobileNumber, phone);
        q.lambda().notIn(DoctorUser::getStatus, UserConstants.DoctorUserStatus.DEL, UserConstants.DoctorUserStatus.LOGOFF);
        DoctorUser doc = getOne(q);
        if (doc != null) {
            if (!"194711".equals(code) || ZekeConstants.ProfilesType.PROD.equals(profilesProperties.getActive())) {
                // 校验验证码
                smsComponent.verifyPhoneCode(phone, code);
            }
            DoctorUser update = new DoctorUser();
            update.setId(doc.getId());
            update.setSalt(CodecUtils.generateSalt());
            update.setPassword(CodecUtils.md5Hex(pwd, update.getSalt()));
            updateById(update);
        } else {
            throw new ZekeException(ZkExceptionEnum.ACCOUNT_NOT_FOUND);
        }
    }

    /**
     * 机构管理员添加医生
     *
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addDoctorUser(DoctorCreateReq req) {
        log.info("管理员添加医生:{}", req);
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo().get();
        if (loginInfo.getType() == LoginInfo.UserType.DOCTOR) {
            throw new ZekeException(ZkExceptionEnum.NO_ERMSSION);
        }
        if (loginInfo.getType() == LoginInfo.UserType.MECHAN) {
            req.setMechanId(loginInfo.getMechanId());
        }
        if (req.getMechanId() == null && req.getQualificationType() != 3) {
            throw new ZekeException(ZkExceptionEnum.PARAMETER_ERROR, "机构id不能为空");
        }
        // 校验手机号是否注册
        QueryWrapper<DoctorUser> q = new QueryWrapper<>();
        q.lambda().eq(DoctorUser::getMobileNumber, req.getMobileNumber());
        q.lambda().notIn(DoctorUser::getStatus, UserConstants.DoctorUserStatus.DEL, UserConstants.DoctorUserStatus.LOGOFF);
        int count = count(q);
        if (count > 0) {
            throw new ZekeException(ZkExceptionEnum.PHONE_IS_EXISTS);
        }
        if (req.getMechanId() != null) {
            MechanRank mechanRank = mechanRankService.getById(req.getMechanId());
            if (mechanRank == null) {
                throw new ZekeException(ZkExceptionEnum.MECHAN_NOT_FOUND);
            }
        }
        Integer qualificationType = req.getQualificationType();
        if (qualificationType.equals(UserConstants.QualificationType.COUNTRY)) {
            checkCountry(req);
        } else if (qualificationType.equals(UserConstants.QualificationType.PRACTICE)) {
            checkPractice(req);
        }
        DoctorUser doctorUser = new DoctorUser();
        doctorUser.setMobileNumber(req.getMobileNumber());
        String salt = CodecUtils.generateSalt();
        doctorUser.setSalt(salt);
        //加密
        if (StringUtils.isNotBlank(req.getPassword())) {
            doctorUser.setPassword(CodecUtils.md5Hex(req.getPassword(), doctorUser.getSalt()));
        } else {
            doctorUser.setPassword(CodecUtils.md5Hex(req.getMobileNumber().substring(5), doctorUser.getSalt()));
        }
        // 写入数据库
        doctorUser.setIsCertification(req.getIsCertification());
        doctorUser.setStatus(UserConstants.DoctorUserStatus.NORMAL);
        if (req.getQualificationType() == UserConstants.QualificationType.PRACTICE || (req.getQualificationType() == UserConstants.QualificationType.COUNTRY && (req.getCertificateType() == UserConstants.CertificateType.ONE || req.getCertificateType() == UserConstants.CertificateType.FIVE || req.getCertificateType() == UserConstants.CertificateType.SEVEN))) {
            doctorUser.setIsPresPower(UserConstants.isPower.OPEN);
        } else {
            doctorUser.setIsPresPower(UserConstants.isPower.NOT_OPEN);
        }
        doctorUser.setCreateDate(LocalDateTime.now());
        doctorUser.setRemark(req.getRemark());
        doctorUser.setCodeRecord(req.getCodeRecord());
        doctorUser.setMechanId(req.getMechanId());
        doctorUser.setOperId(loginInfo.getId());
        doctorUser.setOperName(loginInfo.getName());
        doctorUser.setOperType(loginInfo.getType());
        doctorUser.setType(req.getType());
        doctorUser.setIsCertification(req.getIsCertification());
        int insert = getBaseMapper().insert(doctorUser);
        if (insert <= 0) {
            throw new ZekeException(ZkExceptionEnum.ADD_ERROR);
        }
        req.setDoctorId(doctorUser.getId());
        DoctorUser update = new DoctorUser();
        update.setId(doctorUser.getId());
        update.setInviteCode(HealthConstants.InviteCodePrefixConstants.DOCTOR_PREFIX + SerialNumberUtils.generatorInviteCode(doctorUser.getId(), 5));
        if (StringUtils.isNotBlank(req.getCodeRecord())) {
            req.setCodeRecord(req.getCodeRecord().toUpperCase());
            if (req.getType().equals(UserConstants.DoctorUserType.PUB)) {
                if (req.getCodeRecord().indexOf(HealthConstants.InviteCodePrefixConstants.SALE_PREFIX) != 0) {
                    throw new ZekeException("此邀请码不是销售邀请码");
                } else {
                    AdminSaleInfo adminSaleInfo = saleInviteDoctorService.bindSaleInviteDoctor(req.getCodeRecord(), doctorUser.getId());
                    update.setInviteName(adminSaleInfo.getName());
                    update.setInviteType(UserConstants.UserType.SALE);
                }
            } else if (req.getType().equals(UserConstants.DoctorUserType.PRO)) {
                if (req.getCodeRecord().indexOf(HealthConstants.InviteCodePrefixConstants.DOCTOR_PREFIX) == 0) {
                    QueryWrapper<DoctorUser> qw = new QueryWrapper<>();
                    qw.lambda().eq(DoctorUser::getInviteCode, req.getCodeRecord().toUpperCase());
                    qw.lambda().notIn(DoctorUser::getStatus, UserConstants.DoctorUserStatus.DEL, UserConstants.DoctorUserStatus.LOGOFF);
                    qw.lambda().select(DoctorUser::getId);
                    DoctorUser inviteDoc = getOne(qw);
                    if (inviteDoc == null) {
                        throw new ZekeException(400, "邀请码不存在,请重新输入");
                    }
                    update.setInviteDocId(inviteDoc.getId());
                    update.setInviteType(UserConstants.UserType.DOCTOR);
                    LambdaQueryWrapper<DoctorDetail> detailLambdaQueryWrapper = new LambdaQueryWrapper<>();
                    detailLambdaQueryWrapper.eq(DoctorDetail::getDoctorId, doctorUser.getId());
                    DoctorDetail doctorDetail = doctorDetailService.getOne(detailLambdaQueryWrapper);
                    if (StringUtils.isNotBlank(doctorDetail.getName())) {
                        update.setInviteName(doctorDetail.getName());
                    }
                } else if (req.getCodeRecord().indexOf(HealthConstants.InviteCodePrefixConstants.SALE_PREFIX) == 0) {
                    AdminSaleInfo adminSaleInfo = saleInviteDoctorService.bindSaleInviteDoctor(req.getCodeRecord(), doctorUser.getId());
                    update.setInviteName(adminSaleInfo.getName());
                    update.setInviteType(UserConstants.UserType.SALE);
                } else if (req.getCodeRecord().indexOf(HealthConstants.InviteCodePrefixConstants.AGENT_PREFIX) == 0) {
                    Agent agent = agentDoctorBindService.doctorBindAgent(req.getCodeRecord(), doctorUser.getId());
                    update.setInviteName(agent.getName());
                    update.setInviteType(UserConstants.UserType.AGENT);
                }
            }
        }
        boolean flag = updateById(update);
        if (!flag) {
            throw new ZekeException(ZkExceptionEnum.ADD_ERROR);
        }
        String birthDate = DateUtils.extractYearMonthDayOfIdCard(req.getIdCard());
        if (StringUtils.isNotBlank(birthDate)) {
            req.setBirthDate(LocalDate.parse(birthDate));
        } else {
            req.setBirthDate(DateUtils.byAgeNumAndAgeTypeToDate(req.getAge(), 2));
        }
        //添加医生详细信息
        doctorDetailService.addDoctorDetail(req);
        //提交认证审核
        if (req.getIsCertification() == 0) {
            DoctorDetailApply doctorDetailApply = new DoctorDetailApply();
            BeanUtils.copyProperties(req, doctorDetailApply);
            doctorDetailApply.setDoctorId(doctorUser.getId());
            doctorDetailApply.setStatus(1);
            doctorDetailApply.setType(1);
            doctorDetailApply.setCreateDate(LocalDateTime.now());
            doctorDetailApplyService.save(doctorDetailApply);
        }
    }


    private void checkPractice(DoctorCreateReq req) {
        if (StringUtils.isBlank(req.getPraCertificateNo()) || StringUtils.isBlank(req.getPraCertificatePhoto())) {
            throw new ZekeException("执业医师证书编号/图片不能为空");
        }
    }

    private void checkCountry(DoctorCreateReq req) {
        if (req.getCertificateType() == null) {
            throw new ZekeException("乡村医生证书类型不能为空");
        }
        if (req.getCertificateType().equals(UserConstants.CertificateType.NINE) && StringUtils.isBlank(req.getCertificateName())) {
            throw new ZekeException("乡村医生证书名称不能为空");
        }
        if (StringUtils.isBlank(req.getCertificateUrl())) {
            throw new ZekeException("乡村医生证件路径不能为空");
        }
    }

    /**
     * excel批量添加医生
     *
     * @param file
     */
    @Override
    public DoctorExcelRes addDoctorBatchByExcel(MultipartFile file) {
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo().get();
        if (loginInfo.getType() == LoginInfo.UserType.ADMIN) {
            return adminAddDoctorUserByExcel(file);
        }
        if (loginInfo.getType() == LoginInfo.UserType.MECHAN) {
            return mechanAddDoctorUserByExcel(file);
        }
        return null;
    }

    /**
     * 运营添加 解析EXCEL添加医生
     *
     * @param file
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public DoctorExcelRes adminAddDoctorUserByExcel(MultipartFile file) {
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo().get();
        if (loginInfo.getType() != LoginInfo.UserType.ADMIN) {
            throw new ZekeException(ZkExceptionEnum.NO_ERMSSION);
        }
        if (file == null) {
            throw new ZekeException(400, "请上传文件");
        }
        if (!file.getOriginalFilename().contains(".xlsx") && !file.getOriginalFilename().contains(".xls")) {
            throw new ZekeException(400, "只支持xlsx和xls文件格式");
        }
        List<AdminDoctorCreateBatchReq> list;
        try {
            list = ExtUtils.importExcel(file, 1, 1, AdminDoctorCreateBatchReq.class);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ZekeException(400, "excel解析失败");
        }

        if (list == null || list.isEmpty()) {
            throw new ZekeException(400, "文档数据为空");
        }

        List<String> errList = new ArrayList<>();
        for (AdminDoctorCreateBatchReq user : list) {

            //手动验证数据
            String s = ValidatorUtils.verifyStr(user, AdminDoctorCreateBatchReq.class);
            if (!StringUtils.isBlank(s)) {
                errList.add(user.getName() + ": " + s);
                continue;
            }

            QueryWrapper<DoctorUser> doctorUserQueryWrapper = new QueryWrapper<>();
            doctorUserQueryWrapper.lambda().eq(DoctorUser::getMobileNumber, user.getMobileNumber()).ne(DoctorUser::getStatus, UserConstants.DoctorUserStatus.DEL);
            List<DoctorUser> isPhones = this.getBaseMapper().selectList(doctorUserQueryWrapper);
            if (CollectionUtil.isNotEmpty(isPhones)) {
                errList.add(user.getName() + ": 手机号已存在");
                continue;
            }

            QueryWrapper<MechanRank> mechanRankQueryWrapper = new QueryWrapper<>();
            mechanRankQueryWrapper.lambda().eq(MechanRank::getName, user.getMechanName()).eq(MechanRank::getStatus, MechanConstants.MechanStatus.NORMAL);
            List<MechanRank> mechanRanks = mechanRankService.list(mechanRankQueryWrapper);
            if (CollectionUtil.isNotEmpty(mechanRanks)) {
                errList.add(user.getName() + ": 管理机构不存在");
                continue;
            }
            Integer mechanId = mechanRanks.get(0).getId();

            String birthday = DateUtils.extractYearMonthDayOfIdCard(user.getIdCard());
            if (StringUtils.isBlank(birthday)) {
                errList.add(user.getName() + ": 身份证号格式不正确");
                continue;
            }

            Integer saleId;
            SaleInviteDoctor saleInviteDoctor = null;
            AgentDoctorBind agentDoctorBind = null;
            DoctorUser doctorUser = new DoctorUser();
            if ("慈善账号".equals(user.getType())) {
                if (StringUtils.isNotBlank(user.getSell())) {
                    if (StringUtils.isBlank(user.getSell())) {
                        errList.add(user.getName() + ": 公益账号销售人员不能为空");
                        continue;
                    }
                    //查询销售
                    LambdaQueryWrapper<AdminSaleInfo> wrapper = new LambdaQueryWrapper<>();
                    wrapper.eq(AdminSaleInfo::getName, user.getSell()).eq(AdminSaleInfo::getStatus, UserConstants.DoctorUserStatus.NORMAL);
                    List<AdminSaleInfo> adminSaleInfos = adminSaleInfoService.getBaseMapper().selectList(wrapper);
                    if (CollectionUtil.isEmpty(adminSaleInfos)) {
                        errList.add(user.getName() + ":公益账号销售人员不存在");
                        continue;
                    }
                    if (adminSaleInfos.size() > 1) {
                        Map<String, AdminSaleInfo> adminSaleInfoMap = adminSaleInfos.stream().collect(Collectors.toMap(AdminSaleInfo::getInviteCode, Function.identity()));
                        if (StringUtils.isNotBlank(user.getInviteCode())) {
                            AdminSaleInfo adminSaleInfo = adminSaleInfoMap.get(user.getInviteCode());
                            if (adminSaleInfo == null) {
                                errList.add(user.getName() + ":公益账号销售人员邀请码与填写的邀请码不一致");
                                continue;
                            } else {
                                saleId = adminSaleInfo.getAdminId();
                                String inviteCode = adminSaleInfo.getInviteCode();
                                doctorUser.setCodeRecord(inviteCode);
                                doctorUser.setInviteType(UserConstants.UserType.DOCTOR);
                                doctorUser.setInviteName(adminSaleInfo.getName());
                            }
                        } else {
                            AdminSaleInfo adminSaleInfo = adminSaleInfos.get(0);
                            String inviteCode = adminSaleInfo.getInviteCode();
                            saleId = adminSaleInfo.getAdminId();
                            doctorUser.setCodeRecord(inviteCode);
                            doctorUser.setInviteName(adminSaleInfo.getName());
                            doctorUser.setInviteType(UserConstants.UserType.DOCTOR);
                        }
                    } else {
                        AdminSaleInfo adminSaleInfo = adminSaleInfos.get(0);
                        String inviteCode = adminSaleInfo.getInviteCode();
                        if (StringUtils.isNotBlank(user.getInviteCode()) && !inviteCode.equals(user.getInviteCode())) {
                            errList.add(user.getName() + ":公益账号销售人员邀请码与填写的邀请码不一致");
                            continue;
                        }
                        saleId = adminSaleInfo.getAdminId();
                        doctorUser.setCodeRecord(inviteCode);
                        doctorUser.setInviteName(adminSaleInfo.getName());
                        doctorUser.setInviteType(UserConstants.UserType.DOCTOR);
                    }
                    doctorUser.setType(UserConstants.DoctorUserType.PUB_NO_AUTH);
                    if (saleId != null) {
                        saleInviteDoctor = new SaleInviteDoctor();
                        saleInviteDoctor.setSaleId(saleId);
                        saleInviteDoctor.setStatus(AdminConstants.SaleInviteStatus.NORMAL);
                        saleInviteDoctor.setCreateDate(LocalDateTime.now());
                    }
                }
            } else if ("正式账号".equals(user.getType())) {
                doctorUser.setType(UserConstants.DoctorUserType.PRO);
                if (StringUtils.isNotBlank(user.getInviteCode())) {
                    user.setInviteCode(user.getInviteCode().toUpperCase());
                    if (user.getInviteCode().indexOf(HealthConstants.InviteCodePrefixConstants.DOCTOR_PREFIX) == 0) {
                        QueryWrapper<DoctorUser> qw = new QueryWrapper<>();
                        qw.lambda().eq(DoctorUser::getInviteCode, user.getInviteCode());
                        qw.lambda().notIn(DoctorUser::getStatus, UserConstants.DoctorUserStatus.DEL, UserConstants.DoctorUserStatus.LOGOFF);
                        DoctorUser inviteDoc = getOne(qw);
                        if (inviteDoc == null) {
                            errList.add(user.getName() + ":邀请码不存在");
                            continue;
                        }
                        LambdaQueryWrapper<DoctorDetail> wrapper = new LambdaQueryWrapper<>();
                        wrapper.eq(DoctorDetail::getDoctorId, inviteDoc.getId());
                        DoctorDetail doctorDetail = doctorDetailService.getOne(wrapper);
                        doctorUser.setInviteDocId(inviteDoc.getId());
                        doctorUser.setCodeRecord(user.getInviteCode());
                        if (StringUtils.isNotBlank(doctorDetail.getName())) {
                            doctorUser.setInviteName(doctorDetail.getName());
                        }
                        doctorUser.setInviteType(UserConstants.UserType.DOCTOR);
                    } else if (user.getInviteCode().indexOf(HealthConstants.InviteCodePrefixConstants.SALE_PREFIX) == 0) {
                        QueryWrapper<AdminSaleInfo> qw = new QueryWrapper<>();
                        qw.lambda().eq(AdminSaleInfo::getInviteCode, user.getInviteCode());
                        qw.lambda().ne(AdminSaleInfo::getStatus, AdminConstants.SaleInfoStatus.DEL);
                        AdminSaleInfo adminSaleInfo = adminSaleInfoService.getOne(qw);
                        if (adminSaleInfo == null) {
                            errList.add(user.getName() + ":邀请码不存在");
                            continue;
                        }
                        saleInviteDoctor = new SaleInviteDoctor();
                        saleInviteDoctor.setSaleId(adminSaleInfo.getAdminId());
                        saleInviteDoctor.setStatus(AdminConstants.SaleInviteStatus.NORMAL);
                        saleInviteDoctor.setCreateDate(LocalDateTime.now());
                        doctorUser.setCodeRecord(user.getInviteCode());
                        doctorUser.setInviteName(adminSaleInfo.getName());
                        doctorUser.setInviteType(UserConstants.UserType.SALE);
                    } else if (user.getInviteCode().indexOf(HealthConstants.InviteCodePrefixConstants.AGENT_PREFIX) == 0) {
                        QueryWrapper<Agent> qw = new QueryWrapper<>();
                        qw.lambda().eq(Agent::getInviteCode, user.getInviteCode());
                        qw.lambda().ne(Agent::getStatus, AgentConstants.AgentStatus.DEL);
                        Agent agent = agentService.getOne(qw);
                        if (agent == null) {
                            errList.add(user.getName() + ":邀请码不存在");
                            continue;
                        }
                        agentDoctorBind = new AgentDoctorBind();
                        agentDoctorBind.setAgentId(agent.getId());
                        agentDoctorBind.setStatus(AgentConstants.AgentDoctorBindStatus.NORMAL);
                        agentDoctorBind.setCreateDate(LocalDateTime.now());
                        doctorUser.setCodeRecord(user.getInviteCode());
                        doctorUser.setInviteName(agent.getName());
                        doctorUser.setInviteType(UserConstants.UserType.AGENT);
                    }
                }
            } else {
                errList.add(user.getName() + ": 账号类型数据错误");
                continue;
            }

            doctorUser.setMobileNumber(user.getMobileNumber());
            // 对密码进行加密
            String salt = CodecUtils.generateSalt();
            doctorUser.setSalt(salt);
            doctorUser.setPassword(CodecUtils.md5Hex(user.getMobileNumber().substring(5), doctorUser.getSalt()));
            doctorUser.setIsCertification(UserConstants.IsCertification.not_certification);
            doctorUser.setStatus(UserConstants.DoctorUserStatus.NORMAL);
            doctorUser.setIsPresPower(UserConstants.isPower.NOT_OPEN);
            // 写入数据库
            doctorUser.setCreateDate(LocalDateTime.now());
            doctorUser.setMechanId(mechanId);
            doctorUser.setOperId(loginInfo.getId());
            doctorUser.setOperName(loginInfo.getName());
            //手动开启事务
            TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
            int insert = getBaseMapper().insert(doctorUser);
            if (insert <= 0) {
                errList.add(user.getName() + ": 医生数据添加失败");
                continue;
            }
            if (saleInviteDoctor != null) {
                saleInviteDoctor.setDoctorId(doctorUser.getId());
                int flag = saleInviteDoctorService.getBaseMapper().insert(saleInviteDoctor);
                if (flag <= 0) {
                    errList.add(user.getName() + ": 医生数据添加失败");
                    continue;
                }
            }
            if (agentDoctorBind != null) {
                agentDoctorBind.setDoctorId(doctorUser.getId());
                int flag = agentDoctorBindService.getBaseMapper().insert(agentDoctorBind);
                if (flag <= 0) {
                    errList.add(user.getName() + ": 医生数据添加失败");
                    continue;
                }
            }
            DoctorUser update = new DoctorUser();
            update.setId(doctorUser.getId());
            update.setInviteCode(HealthConstants.InviteCodePrefixConstants.DOCTOR_PREFIX + SerialNumberUtils.generatorInviteCode(doctorUser.getId(), 5));
            boolean flag = this.updateById(update);
            if (!flag) {
                log.info("~更新医生邀请码失败~");
            }
            user.setDoctorId(doctorUser.getId());
            DoctorCreateReq reqDetail = new DoctorCreateReq();
            BeanUtils.copyProperties(user, reqDetail);
            reqDetail.setMechanId(mechanId);
            AddrUtils.AddrEntity addrEntity = AddrUtils.getAddrEntity(user.getAddr());
            reqDetail.setProvince(addrEntity.getProvince());
            reqDetail.setCity(addrEntity.getCity());
            reqDetail.setDistrict(addrEntity.getDistrict());
            reqDetail.setOrganization(user.getOrganization());
            reqDetail.setBirthDate(LocalDate.parse(birthday));
            try {
                //添加医生详细信息
                doctorDetailService.addDoctorDetail(reqDetail);
                //手动提交事务
                dataSourceTransactionManager.commit(transactionStatus);
            } catch (Exception e) {
                e.printStackTrace();
                errList.add(user.getName() + ": 医生详情添加失败");
                //异常回滚
                dataSourceTransactionManager.rollback(transactionStatus);
            }
        }
        DoctorExcelRes doctorExcelRes = new DoctorExcelRes();
        doctorExcelRes.setSuccessNum(list.size() - errList.size());
        doctorExcelRes.setFailNum(errList.size());
        doctorExcelRes.setFailInfo(String.join(",", errList));
        return doctorExcelRes;
    }

    /**
     * 机构添加 解析EXCEL添加医生
     *
     * @param file
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public DoctorExcelRes mechanAddDoctorUserByExcel(MultipartFile file) {
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo(LoginInfo.UserType.MECHAN).get();
        if (file == null) {
            throw new ZekeException(400, "请上传文件");
        }
        if (!file.getOriginalFilename().contains(".xlsx") && !file.getOriginalFilename().contains(".xls")) {
            throw new ZekeException(400, "只支持xlsx和xls文件格式");
        }
        List<MechanCreateDoctorBatchReq> list = new ArrayList<>();
        try {
            list = ExtUtils.importExcel(file, 1, 1, MechanCreateDoctorBatchReq.class);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ZekeException(400, "文档数据为空");
        }
        if (list == null || list.isEmpty()) {
            throw new ZekeException(400, "文档数据为空");
        }
        // 校验手机号是否注册
        QueryWrapper<DoctorUser> q = new QueryWrapper<>();
        q.lambda().in(DoctorUser::getMobileNumber, list.stream().map(MechanCreateDoctorBatchReq::getMobileNumber).collect(Collectors.toList()));
        q.lambda().ne(DoctorUser::getStatus, UserConstants.DoctorUserStatus.DEL);
        q.lambda().select(DoctorUser::getMobileNumber);
        List<DoctorUser> exists = list(q);
        if (exists != null && !exists.isEmpty()) {
            throw new ZekeException(400, "医生手机号已存在:" + StringUtils.join(exists.stream().map(DoctorUser::getMobileNumber).collect(Collectors.toList()), ","));
        }
        MechanRank rank = mechanRankService.getById(loginInfo.getMechanId());
        for (MechanCreateDoctorBatchReq user : list) {
            //对象属性都为null 则过滤 Excel转入obj已有判断, 这里就不需要
//            if (ObjectUtils.objCheckIsNull(user)) {
//                continue;
//            }
            //手动验证数据
            ValidatorUtils.verify(user, AdminDoctorCreateBatchReq.class);
            if (!StringUtils.isBlank(user.getIdCard())) {
                user.setBirthDate(LocalDate.parse(IDCardUtils.getBirthday(user.getIdCard())));
            }
            DoctorUser doctorUser = new DoctorUser();
            doctorUser.setMobileNumber(user.getMobileNumber());
            // 对密码进行加密
            String salt = CodecUtils.generateSalt();
            doctorUser.setSalt(salt);
            doctorUser.setPassword(CodecUtils.md5Hex(user.getMobileNumber().substring(5), doctorUser.getSalt()));
            doctorUser.setStatus(UserConstants.DoctorUserStatus.NORMAL);
            // 写入数据库
            doctorUser.setCreateDate(LocalDateTime.now());
            doctorUser.setMechanId(rank.getId());
            doctorUser.setOperId(loginInfo.getId());
            doctorUser.setOperName(loginInfo.getName());
            int insert = getBaseMapper().insert(doctorUser);
            if (insert <= 0) {
                throw new ZekeException(ZkExceptionEnum.ADD_ERROR);
            }
            DoctorUser update = new DoctorUser();
            update.setId(doctorUser.getId());
            update.setInviteCode(HealthConstants.InviteCodePrefixConstants.DOCTOR_PREFIX + SerialNumberUtils.generatorInviteCode(doctorUser.getId(), 5));
            boolean flag = this.updateById(update);
            if (!flag) {
                log.info("更新医生邀请码失败");
            }
            user.setDoctorId(doctorUser.getId());
            DoctorCreateReq reqDetail = new DoctorCreateReq();
            BeanUtils.copyProperties(user, reqDetail);
            reqDetail.setOrganization(rank.getName());
            //添加医生详细信息
            doctorDetailService.addDoctorDetail(reqDetail);
        }
        DoctorExcelRes doctorExcelRes = new DoctorExcelRes();
        doctorExcelRes.setSuccessNum(list.size());
        return doctorExcelRes;
    }

    /**
     * 医生修改密码
     *
     * @param id 医生ID
     * @param op 原密码
     * @param np 新密码
     * @return
     */
    @Override
    @Transactional
    public void changePassword(Integer id, String op, String np) {
        if (id == null) {
            id = UserInterceptor.getLoginUserInfo().get().getId();
        }
        QueryWrapper<DoctorUser> q = new QueryWrapper<>();
        q.lambda().eq(DoctorUser::getId, id);
        DoctorUser doc = getOne(q);
        if (doc == null) {
            throw new ZekeException(ZkExceptionEnum.ACCOUNT_NOT_FOUND);
        }
        // 比较原密码是否正确
        if (!CodecUtils.md5Hex(op, doc.getSalt()).equals(doc.getPassword())) {
            throw new ZekeException(ZkExceptionEnum.OLD_PSW_IS_NOT_CONRRECT);
        }
        doc.setPassword(CodecUtils.md5Hex(np, doc.getSalt()));
        boolean update = updateById(doc);
        if (!update) {
            throw new ZekeException(ZkExceptionEnum.OPER_ERROR);
        }
    }

    /**
     * 医生找回密码
     *
     * @param mobileNumber 手机号
     * @param code         手机验证码
     * @param np           新密码
     * @param cp           确认新密码
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void findPassword(String mobileNumber, String code, String np, String cp, HttpServletRequest request) {
        String client = request.getHeader("client");
        // 判断两次密码是否一致
        if (!np.equals(cp)) {
            throw new ZekeException(400, "密码不一致");
        }
        if (!"194711".equals(code) || ZekeConstants.ProfilesType.PROD.equals(profilesProperties.getActive())) {
            // 校验验证码
            smsComponent.verifyPhoneCode(mobileNumber, code);
        }
        // 通过手机号查询账号
        QueryWrapper<DoctorUser> q = new QueryWrapper<>();
        q.lambda().eq(DoctorUser::getMobileNumber, mobileNumber);
        q.lambda().notIn(DoctorUser::getStatus, UserConstants.DoctorUserStatus.DEL, UserConstants.DoctorUserStatus.LOGOFF);
        DoctorUser doc = getOne(q);
        if (doc == null) {
            //PC端需要注册医生账号
            if (HealthConstants.ClientType.HEALTHPC.equals(client)) {
                savDoctorUser(mobileNumber, np);
                return;
            } else {
                throw new ZekeException(ZkExceptionEnum.ACCOUNT_NOT_FOUND);
            }
        } else {
            // 更新新密码
            doc.setPassword(CodecUtils.md5Hex(np, doc.getSalt()));
            boolean update = updateById(doc);
            if (!update) {
                throw new ZekeException(ZkExceptionEnum.OPER_ERROR);
            }
        }
    }

    private void savDoctorUser(String mobileNumber, String np) {
        DoctorUser doc = new DoctorUser();
        doc.setType(1);
        doc.setMobileNumber(mobileNumber);
        String salt = CodecUtils.generateSalt();
        doc.setSalt(salt);
        doc.setPassword(CodecUtils.md5Hex(np, doc.getSalt()));
        doc.setIsCertification(UserConstants.IsCertification.not_certification);
        doc.setIsPresPower(0);
        doc.setStatus(UserConstants.DoctorUserStatus.NORMAL);
        doc.setCreateDate(LocalDateTime.now());
        doc.setMechanId(0);
        doc.setCreateAccountType(2);
        save(doc);
        DoctorUser update = new DoctorUser();
        update.setId(doc.getId());
        update.setInviteCode(HealthConstants.InviteCodePrefixConstants.DOCTOR_PREFIX + SerialNumberUtils.generatorInviteCode(doc.getId(), 5));
        updateById(update);
        DoctorDetail dd = new DoctorDetail();
        dd.setDoctorId(doc.getId());
        dd.setMobileNumber(mobileNumber);
        dd.setName(mobileNumber);
        dd.setStatus(UserConstants.DoctorDetailStatus.NORMAL);
        dd.setCreateDate(LocalDateTime.now());
        doctorDetailService.save(dd);
    }

    /**
     * 设置密码
     *
     * @param password
     * @param passwords
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void setPassWord(String password, String passwords) {
        if (!password.equals(passwords)) {
            throw new ZekeException(400, "两次密码不一致");
        }
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo(LoginInfo.UserType.DOCTOR).get();
        QueryWrapper<DoctorUser> qw = new QueryWrapper<>();
        qw.lambda().eq(DoctorUser::getId, loginInfo.getId());
        qw.lambda().ne(DoctorUser::getStatus, UserConstants.DoctorUserStatus.DEL);
        DoctorUser doc = getOne(qw);
        if (doc == null) {
            throw new ZekeException(ZkExceptionEnum.ACCOUNT_NOT_FOUND);
        }
        if (!StringUtils.isBlank(doc.getPassword())) {
            throw new ZekeException(400, "已存在密码,无法再次设置,请进行修改密码操作");
        }
        DoctorUser update = new DoctorUser();
        update.setId(doc.getId());
        update.setSalt(CodecUtils.generateSalt());
        update.setPassword(CodecUtils.md5Hex(password, update.getSalt()));
        boolean flag = updateById(update);
        if (!flag) {
            throw new ZekeException(ZkExceptionEnum.OPER_ERROR);
        }
    }

    /**
     * 修改医生状态
     * @param id 医生id
     * @param status         状态
     */
//    @Override
//    public void updateStatus(Integer id, Integer status) {
//        DoctorUser doctorUser = getById(id);
//        if (doctorUser == null){
//            throw new ZekeException(ZkExceptionEnum.ACCOUNT_IS_EXISTS);
//        }
//        if (doctorUser.getStatus().equals(status)){
//            return;
//        }
//        if (status != UserConstants.DoctorUserStatus.DISABLEMECHAN
//                || status != UserConstants.DoctorUserStatus.DEL){
//            throw new ZekeException(400,"状态数据错误");
//        }
//        doctorUser.setStatus(status);
//        boolean i = updateById(doctorUser);
//        if (!i){
//            throw new ZekeException(ZkExceptionEnum.UPDATE_ERROR);
//        }
//    }


    /**
     * 校验账号状态
     *
     * @param doc
     */
    @Override
    public void switchStatus(DoctorUser doc) {
        switch (doc.getStatus()) {
            case UserConstants.DoctorUserStatus.DISABLEMECHAN:
            case UserConstants.DoctorUserStatus.DISABLEADMIN:
                // 运营后台停用账号
                // 机构停用账号
                throw new ZekeException(ZkExceptionEnum.ACCOUNT_IS_FORBIDDEN.getCode(), doc, ZkExceptionEnum.ACCOUNT_IS_FORBIDDEN.getMsg());

            case UserConstants.DoctorUserStatus.DEL:
            case UserConstants.DoctorUserStatus.LOGOFF:
                // 已删除
                throw new ZekeException(ZkExceptionEnum.ACCOUNT_IS_DELETED.getCode(), doc, ZkExceptionEnum.ACCOUNT_IS_DELETED.getMsg());
            default:
        }
    }

    /**
     * 查询医生列表后台
     *
     * @param req
     * @return
     */
    @Override
    public IPage<DoctorSysListRes> queryDoctorListSys(DoctorSysListReq req) {
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo().get();
        if (loginInfo.getType() == LoginInfo.UserType.DOCTOR) {
            throw new ZekeException(ZkExceptionEnum.NO_ERMSSION);
        }
        if (loginInfo.getType() == LoginInfo.UserType.MECHAN) {
            req.setMechanIdStr(new String[]{String.valueOf(loginInfo.getMechanId())});
        }
        //Assert.verifyStatusIdsParam(req.getTapId(), "tapId", false);
        if (loginInfo.getType() == LoginInfo.UserType.ADMIN && !StringUtils.isBlank(req.getMechanIds())) {
            req.setMechanIdStr(req.getMechanIds().split(","));
        }
        IPage<DoctorSysListRes> iPage = req.pagePojo();
        if (StringUtils.isBlank(req.getStatus())) {
            req.setStatus(UserConstants.DoctorUserStatus.SEL_ALL);
        }
        if (!StringUtils.isBlank(req.getStatus())) {
            req.setStatusStr(req.getStatus().split(","));
        }
        if (StringUtils.isNotBlank(req.getTypes())) {
            req.setTypesStr(req.getTypes().split(","));
        }
        iPage = getBaseMapper().queryDoctorListSys(iPage, req);
        return iPage;
    }

    /**
     * 导出医生列表后台
     *
     * @param req
     * @param response
     */
    @Override
    public void exportDoctorUserSystemExcel(DoctorSysListReq req, HttpServletResponse response) {
        int no = 1;
        req.setPage(1);
        req.setPageSize(100);
        IPage<DoctorSysListRes> iPage;
        List<DoctorExportSysListRes> listRes = new ArrayList<>(100);
        do {
            iPage = queryDoctorListSys(req);
            for (DoctorSysListRes item : iPage.getRecords()) {
                DoctorExportSysListRes res = new DoctorExportSysListRes();
                BeanUtils.copyProperties(item, res);
                res.setType(item.getType() == 1 ? "社会账号" : "慈善账号");
                res.setIsCertification(item.getIsCertification() == 1 ? "是" : "否");
                item.setQualificationType(item.getQualificationType() == null ? 0 : item.getQualificationType());
                switch (item.getQualificationType()) {
                    case UserConstants.QualificationType.COUNTRY:
                        res.setQualificationType("乡村医生");
                        break;
                    case UserConstants.QualificationType.PRACTICE:
                        res.setQualificationType("执业医师");
                        break;
                    case UserConstants.QualificationType.STUDENT:
                        res.setQualificationType("医学生");
                        break;
                    case UserConstants.QualificationType.PHARMACISTS:
                        res.setQualificationType("中药师");
                        break;
                    case UserConstants.QualificationType.FAN:
                        res.setQualificationType("爱好者");
                        break;
                    default:
                        res.setQualificationType("无");
                }
                res.setStatus(item.getStatus() == 1 ? "正常" : "异常");
                res.setNo(no);
                listRes.add(res);
                no++;
            }
            req.setPage(req.getPage() + 1);
        } while (iPage.getCurrent() < iPage.getPages());
        String title = "医生账号数据";
        try {
            ExtUtils.exportExcel(listRes, title, title, DoctorExportSysListRes.class, title, true, response);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 查询医生邀请名单列表
     *
     * @param req
     * @return
     */
    @Override
    public IPage<DoctorInvitePageRes> queryDoctorInvitePage(DoctorInvitePageReq req) {
        IPage<DoctorInvitePageRes> iPage = req.pagePojo();
        iPage = getBaseMapper().queryDoctorInvitePage(iPage, req);
        return iPage;
    }

    /**
     * 重置医生密码
     *
     * @param doctorId
     */
    @Override
    public void resetDoctorPwd(Integer doctorId) {
        DoctorUser du = getById(doctorId);
        if (du == null || du.getStatus() == UserConstants.DoctorUserStatus.DEL || du.getStatus() == UserConstants.DoctorUserStatus.LOGOFF) {
            throw new ZekeException(400, "医生不存在");
        }
        du.setSalt(CodecUtils.generateSalt());
        du.setPassword(CodecUtils.md5Hex(du.getMobileNumber().substring(du.getMobileNumber().length() - 6), du.getSalt()));
        updateById(du);
    }

    /**
     * 确认手机号是否存在
     *
     * @param phone
     * @return
     */
    @Override
    public boolean checkPhoneUser(String phone, Integer type) {
        if (type == LoginInfo.UserType.DOCTOR) {
            QueryWrapper<DoctorUser> qw = new QueryWrapper<>();
            qw.lambda().eq(DoctorUser::getMobileNumber, phone).notIn(DoctorUser::getStatus, UserConstants.DoctorUserStatus.DEL, UserConstants.DoctorUserStatus.LOGOFF);
            List<DoctorUser> list = list(qw);
            return list != null && !list.isEmpty();
        }
        QueryWrapper<MechanUser> qs = new QueryWrapper<>();
        qs.lambda().eq(MechanUser::getPhone, phone).ne(MechanUser::getStatus, MechanConstants.MechanUserStatus.DEL);
        List<MechanUser> mechanUsers = mechanUserService.list(qs);
        return mechanUsers != null && !mechanUsers.isEmpty();
    }

    /**
     * 查询openid通过code
     *
     * @param code
     * @return
     */
    @Override
    public String queryOpenidByCode(String code) {
        String url = String.format(wechatConfig.getWebPageAuthorizationAccessTokenUrl(), wechatPayConfig.getAppId(), wechatPayConfig.getMchKey(), code);
        log.info("url:{}", url);
        String result = restTemplate.getForObject(url, String.class);
        JSONObject jsonObject = JSONObject.parseObject(result);
        log.info("网页登录返回:{}", jsonObject);
        if (jsonObject.get("errcode") != null) {
            String msg = jsonObject.getString("errmsg");
            throw new ZekeException(ZkExceptionEnum.WECHAT_LOGIN_ERROR, msg);
        }
        return jsonObject.getString("openid");
    }

    /**
     * 注册医生账号
     *
     * @param request
     * @param response
     * @param req
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void registryDoctorUser(HttpServletRequest request, HttpServletResponse response, DoctorRegistryReq req) {
        if (!"194711".equals(req.getCode()) || ZekeConstants.ProfilesType.PROD.equals(profilesProperties.getActive())) {
            // 校验验证码
            smsComponent.verifyPhoneCode(req.getPhone(), req.getCode());
        }
        DoctorUser inviteDoc = null;
        if (!StringUtils.isBlank(req.getInviteCode())) {
            req.setInviteCode(req.getInviteCode().toUpperCase());
            if (req.getInviteCode().indexOf(HealthConstants.InviteCodePrefixConstants.DOCTOR_PREFIX) == 0) {
                QueryWrapper<DoctorUser> qw = new QueryWrapper<>();
                qw.lambda().eq(DoctorUser::getInviteCode, req.getInviteCode().toUpperCase());
                qw.lambda().notIn(DoctorUser::getStatus, UserConstants.DoctorUserStatus.DEL, UserConstants.DoctorUserStatus.LOGOFF);
                qw.lambda().select(DoctorUser::getId);
                inviteDoc = getOne(qw);
                if (inviteDoc == null) {
                    throw new ZekeException(400, "邀请码不存在,请重新输入");
                }
            }
        }
        QueryWrapper<DoctorUser> qw = new QueryWrapper<>();
        qw.lambda().eq(DoctorUser::getMobileNumber, req.getPhone());
        qw.lambda().notIn(DoctorUser::getStatus, UserConstants.DoctorUserStatus.DEL, UserConstants.DoctorUserStatus.LOGOFF);
        int count = count(qw);
        if (count > 0) {
            throw new ZekeException(400, "手机号已存在");
        }
        if (!req.getPassword().equals(req.getVerifyPassword())) {
            throw new ZekeException("两次密码不一致，请重新输入");
        }
        DoctorUser doc = new DoctorUser();
        doc.setMobileNumber(req.getPhone());
        if (inviteDoc != null) {
            doc.setInviteDocId(inviteDoc.getId());
            doc.setInviteType(UserConstants.UserType.DOCTOR);
            doc.setCodeRecord(req.getInviteCode());
            LambdaQueryWrapper<DoctorDetail> detailLambdaQueryWrapper = new LambdaQueryWrapper<>();
            detailLambdaQueryWrapper.eq(DoctorDetail::getDoctorId, inviteDoc.getId());
            DoctorDetail doctorDetail = doctorDetailService.getOne(detailLambdaQueryWrapper);
            if (doctorDetail != null) {
                doc.setInviteName(doctorDetail.getName());
            }
        }
        //加密
        String salt = CodecUtils.generateSalt();
        doc.setSalt(salt);
        doc.setPassword(CodecUtils.md5Hex(req.getPassword(), doc.getSalt()));
        doc.setType(req.getType());
        doc.setStatus(UserConstants.DoctorUserStatus.NORMAL);
        if (req.getIsAuth() != null && req.getIsAuth() == 1) {
            doc.setIsCertification(UserConstants.IsCertification.certification);
            doc.setIsPresPower(1);
        } else {
            doc.setIsCertification(UserConstants.IsCertification.not_certification);
        }
        if (req.getMechanId() != null) {
            doc.setMechanId(req.getMechanId());
        } else {
            doc.setMechanId(0);
        }
        doc.setCreateDate(LocalDateTime.now());
        boolean flag = save(doc);
        if (!flag) {
            throw new ZekeException(ZkExceptionEnum.ADD_ERROR);
        }
        req.setDoctorId(doc.getId());
        doctorDetailService.registryDoctorUser(req);
        DoctorUser update = new DoctorUser();
        update.setId(doc.getId());
        update.setInviteCode(HealthConstants.InviteCodePrefixConstants.DOCTOR_PREFIX + SerialNumberUtils.generatorInviteCode(doc.getId(), 5));
        if (!StringUtils.isBlank(req.getModel())) {
            setAppLogin(request, req.getPhone(), req.getModel(), doc.getPassword());
        }
        LoginInfo loginInfo = new LoginInfo(doc.getId(), doc.getMechanId(), LoginInfo.UserType.DOCTOR, req.getName(), jwtUtils.getUserAgent(request));
        jwtUtils.createToken(loginInfo, request, response);
        //信任当前设备/刷新设备最后一次登录时间
        trustDeviceService.trustDevice(doc.getId(), LoginInfo.UserType.DOCTOR, req.getModel(), req.getCid());
        if (!StringUtils.isBlank(req.getInviteCode())) {
            if (req.getInviteCode().indexOf(HealthConstants.InviteCodePrefixConstants.SALE_PREFIX) == 0) {
                AdminSaleInfo adminSaleInfo = saleInviteDoctorService.bindSaleInviteDoctor(req.getInviteCode(), doc.getId());
                update.setInviteType(UserConstants.UserType.SALE);
                update.setCodeRecord(req.getInviteCode());
                log.info("邀请人名字:" + adminSaleInfo.getName());
                update.setInviteName(adminSaleInfo.getName());
            } else if (req.getInviteCode().indexOf(HealthConstants.InviteCodePrefixConstants.AGENT_PREFIX) == 0) {
                Agent agent = agentDoctorBindService.doctorBindAgent(req.getInviteCode(), doc.getId());
                update.setInviteType(UserConstants.UserType.AGENT);
                update.setCodeRecord(req.getInviteCode());
                log.info("邀请人名字:" + agent.getName());
                update.setInviteName(agent.getName());
            }
        }
        flag = updateById(update);
        if (!flag) {
            throw new ZekeException(ZkExceptionEnum.ADD_ERROR);
        }
        //注册成功发放积分
        String params = IntegralConstants.IntegralType.ONE_TYPE + "-" + doc.getId();
        log.info("医生注册奖励积分,参数:{}", params);
        rabbitSend.sendMessage(MQConfig.INTEGRAL_EXCHANGE, MQConfig.INTEGRAL_QUEUE, params);
        if (inviteDoc != null && StringUtils.isNotBlank(req.getInviteCode()) && req.getInviteCode().indexOf(HealthConstants.InviteCodePrefixConstants.DOCTOR_PREFIX) == 0) {
            String params2 = IntegralConstants.IntegralType.FOUR_TYPE + "-" + inviteDoc.getId();
            log.info("邀请医生注册奖励积分,参数:{}", params2);
            rabbitSend.sendMessage(MQConfig.INTEGRAL_EXCHANGE, MQConfig.INTEGRAL_QUEUE, params2);
        }
        if (doc.getType() == 1) {
            savePresenterNum(1, 1, doc.getId());
        } else if (doc.getType() == 2) {
            saveDoctorEquity(doc.getId());
        }
        DelayMqVo<Integer> delayMqVo = new DelayMqVo<>();
        delayMqVo.setT(doc.getId());
        delayMqVo.setType(DelayMqVo.DelayMqVoType.DOCTOR_NO_AUTH);
        //毫秒级 48小时
        long ms = 1000 * 60 * 60 * 48;
        rabbitSend.sendMessageDelay(MQConfig.DELAY_EXCHANGE, MQConfig.DELAY_KEY, delayMqVo, ms);
    }


    private void saveDoctorEquity(Integer doctorId) {
        LambdaQueryWrapper<ProductGiveRule> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ProductGiveRule::getAccountType, 2)
                .eq(ProductGiveRule::getStatus, 1)
                .eq(ProductGiveRule::getNumType, 2)
                .eq(ProductGiveRule::getType, 1);
        ProductGiveRule productGiveRule = productGiveRuleService.getOne(queryWrapper);
        if (productGiveRule != null) {
            DoctorEquity doctorEquity = new DoctorEquity();
            doctorEquity.setDoctorId(doctorId)
                    .setCreateDate(LocalDateTime.now())
                    .setExperienceDays(productGiveRule.getNum())
                    .setType(2)
                    .setEndDate(LocalDateTime.now().plusDays(productGiveRule.getNum()))
                    .setAccumulateDay(productGiveRule.getNum())
                    .setFirstMonth(0)
                    .setExpirDate(LocalDateTime.now().plusDays(productGiveRule.getNum()));
            Boolean flag = doctorEquityService.save(doctorEquity);
            if (!flag) {
                throw new ZekeException("保存失败");
            }
        }
    }


    @Override
    public void savePresenterNum(Integer accountType, Integer accountAuth, Integer doctorId) {
        LambdaQueryWrapper<ProductGiveRule> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ProductGiveRule::getAccountType, accountType).eq(ProductGiveRule::getAccountAuth, accountAuth).eq(ProductGiveRule::getStatus, 1).eq(ProductGiveRule::getNum, 1).last("limit 1");
        ProductGiveRule productGiveRule = productGiveRuleService.getOne(queryWrapper);
        if (productGiveRule == null) {
            return;
        }
        if (productGiveRule.getNum() <= 0) {
            return;
        }
        List<ProductGiveRecord> list = new ArrayList<>();
        for (int i = 0; i < productGiveRule.getNum(); i++) {
            ProductGiveRecord productGiveRecord = new ProductGiveRecord();
            productGiveRecord.setCreateDate(LocalDateTime.now())
                    .setDoctorId(doctorId)
                    .setStatus(1)
                    .setReceive(1)
                    .setIsUse(productGiveRule.getAccrual())
                    .setIsCreateOrder(productGiveRule.getGenOrder());
            if (accountAuth == 1) {
                productGiveRecord.setIsAuth(0);
            } else if (accountAuth == 2) {
                productGiveRecord.setIsAuth(1);
            }
            list.add(productGiveRecord);
        }
        if (CollectionUtil.isNotEmpty(list)) {
            Boolean flag1 = productGiveRecordService.saveBatch(list);
            if (!flag1) {
                log.info("赠送失败");
            }
        }
    }

    /**
     * 医生认证情况短信
     *
     * @param doctorId
     */
    @Override
    public void doctorUserAuthSms(Integer doctorId) {
        DoctorUser doctorUser = getById(doctorId);
        if (doctorUser == null) {
            return;
        }
        if (doctorUser.getIsCertification() == 0) {
            Map<String, String> msg = new HashMap<>(5);
            msg.put("phone", doctorUser.getMobileNumber());
            msg.put("param_type", SmsConstants.ParamType.JSON);
            msg.put("sms_template", SmsConstants.Template.REGISTER_AUTH);
            rabbitSend.sendMessage(MQConfig.SMS_EXCHANGE, MQConfig.SMS_KEY, msg);
        }
    }

    @Autowired
    private IWebSocketPrivateGroupService webSocketPrivateGroupService;

    @Override
    public Integer queryWebSocketGroupId() {
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo().get();
        Integer doctorId = loginInfo.getId();
        LambdaQueryWrapper<WebSocketPrivateGroup> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(WebSocketPrivateGroup::getDoctorId, doctorId)
                .in(WebSocketPrivateGroup::getStatus, 1, 2)
                .orderByAsc(WebSocketPrivateGroup::getId)
                .last("limit 1");
        WebSocketPrivateGroup webSocketPrivateGroup = webSocketPrivateGroupService.getOne(queryWrapper);
        if (webSocketPrivateGroup != null) {
            return webSocketPrivateGroup.getId();
        }
        WebSocketPrivateGroup webSocketPrivateGroup2 = new WebSocketPrivateGroup();
        webSocketPrivateGroup2.setDoctorId(loginInfo.getId())
                .setCreateDate(LocalDateTime.now()).setStatus(1);
        Boolean flag = webSocketPrivateGroupService.save(webSocketPrivateGroup2);
        if (!flag) {
            throw new ZekeException("问专家失败,请联系客服");
        }
        return webSocketPrivateGroup2.getId();
    }

    /**
     * 修改医生账号信息 内部service使用
     *
     * @param du
     */
    @Override
    public void updateDoctorUser(DoctorUser du) {
        if (du.getId() == null) {
            return;
        }
        updateById(du);
    }

    /**
     * 初始化医生邀请码
     */
    @Override
    public void initDoctorUserInviteCode() {
        QueryWrapper<DoctorUser> qw = new QueryWrapper<>();
        qw.lambda().isNull(DoctorUser::getInviteCode);
        qw.lambda().select(DoctorUser::getId);
        List<DoctorUser> list = list(qw);
        for (DoctorUser item : list) {
            item.setInviteCode(HealthConstants.InviteCodePrefixConstants.DOCTOR_PREFIX + SerialNumberUtils.generatorInviteCode(item.getId(), 5));
        }
        updateBatchById(list);
    }

    /**
     * 获取医生账号类型
     *
     * @param docId
     * @return
     */
    @Override
    public Integer getDoctorUserType(Integer docId) {
        QueryWrapper<DoctorUser> qw = new QueryWrapper<>();
        qw.lambda().eq(DoctorUser::getId, docId);
        qw.lambda().select(DoctorUser::getType);
        DoctorUser doctorUser = getOne(qw);
        return doctorUser == null ? -1 : doctorUser.getType();
    }

    /**
     * 获取医生账号机构id
     *
     * @param docId
     * @return
     */
    @Override
    public Integer getDoctorUserMechanId(Integer docId) {
        QueryWrapper<DoctorUser> qw = new QueryWrapper<>();
        qw.lambda().eq(DoctorUser::getId, docId);
        qw.lambda().select(DoctorUser::getMechanId);
        DoctorUser doctorUser = getOne(qw);
        return doctorUser == null ? -1 : doctorUser.getMechanId();
    }

    /**
     * 获取医生名称
     *
     * @param docId
     * @return
     */
    @Override
    public String getDoctorUserName(Integer docId) {
        QueryWrapper<DoctorDetail> qw = new QueryWrapper<>();
        qw.lambda().eq(DoctorDetail::getDoctorId, docId);
        qw.lambda().select(DoctorDetail::getName);
        DoctorDetail doctorDetail = doctorDetailService.getOne(qw);
        return doctorDetail == null ? "" : doctorDetail.getName();
    }

    /**
     * 获取医生手机号
     *
     * @param docId
     * @return
     */
    @Override
    public String getDoctorUserPhone(Integer docId) {
        QueryWrapper<DoctorDetail> qw = new QueryWrapper<>();
        qw.lambda().eq(DoctorDetail::getDoctorId, docId);
        qw.lambda().select(DoctorDetail::getMobileNumber);
        DoctorDetail doctorDetail = doctorDetailService.getOne(qw);
        return doctorDetail == null ? "" : doctorDetail.getMobileNumber();
    }

    /**
     * 获取医生医疗机构
     *
     * @param docId
     * @return
     */
    @Override
    public String getDoctorUserOrganization(Integer docId) {
        QueryWrapper<DoctorDetail> qw = new QueryWrapper<>();
        qw.lambda().eq(DoctorDetail::getDoctorId, docId);
        qw.lambda().select(DoctorDetail::getOrganization);
        DoctorDetail doctorDetail = doctorDetailService.getOne(qw);
        return doctorDetail == null ? "" : doctorDetail.getOrganization();
    }

    /**
     * 获取医生账号处方权情况
     *
     * @param docId
     * @return
     */
    @Override
    public Integer getDoctorUserPresPower(Integer docId) {
        QueryWrapper<DoctorUser> qw = new QueryWrapper<>();
        qw.lambda().eq(DoctorUser::getId, docId);
        qw.lambda().select(DoctorUser::getIsPresPower);
        DoctorUser doctorUser = getOne(qw);
        return doctorUser == null ? 0 : doctorUser.getIsPresPower();
    }

    /**
     * 查询医生处方权权限业务数据
     *
     * @param docId
     * @return
     */
    @Override
    public DoctorUserPresBoRes queryDoctorUserPresPowerBoRes(Integer docId) {
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo(LoginInfo.UserType.DOCTOR).get();
        if (docId == null) {
            docId = loginInfo.getId();
        }
        QueryWrapper<DoctorUser> qw = new QueryWrapper<>();
        qw.lambda().eq(DoctorUser::getId, docId);
        qw.lambda().select(DoctorUser::getIsPresPower, DoctorUser::getIsOpenReviewBo);
        DoctorUser doctorUser = getOne(qw);
        DoctorUserPresBoRes res = new DoctorUserPresBoRes();
        if (doctorUser != null) {
            BeanUtils.copyProperties(doctorUser, res);
        }
        return res;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateMechanName() {
        List<DoctorMechanNameRes> doctorMechanNameRes = this.baseMapper.selectMechanNameList();
        List<DoctorDetail> doctorDetailsList = doctorDetailService.list();
        if (CollectionUtil.isNotEmpty(doctorMechanNameRes)) {
            Map<Integer, String> map = doctorMechanNameRes.stream().collect(HashMap::new, (k, v) -> k.put(v.getDocotorId(), v.getOrganization()), HashMap::putAll);
            if (CollectionUtil.isNotEmpty(doctorDetailsList)) {
                for (DoctorDetail doctorDetail : doctorDetailsList) {
                    String organization = map.get(doctorDetail.getDoctorId());
                    if (StringUtils.isNotBlank(organization)) {
                        doctorDetail.setOrganization(organization);
                    }
                }
                doctorDetailService.updateBatchById(doctorDetailsList);
            }
        }
    }

    /**
     * 修改医生账号类型
     *
     * @param doctorId
     * @param type
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDoctorUserType(Integer doctorId, Integer type) {
        UserInterceptor.getLoginUserInfo(LoginInfo.UserType.ADMIN);
        QueryWrapper<DoctorUser> qw = new QueryWrapper<>();
        qw.lambda().eq(DoctorUser::getId, doctorId);
        qw.lambda().notIn(DoctorUser::getStatus, UserConstants.DoctorUserStatus.DEL, UserConstants.DoctorUserStatus.LOGOFF);
        DoctorUser doctorUser = getOne(qw);
        if (doctorUser == null) {
            throw new ZekeException(400, "医生账号不存在");
        }
        if (doctorUser.getType().equals(type)) {
            return;
        }
        boolean flag = ObjectUtils.refrect(UserConstants.DoctorUserType.class, type);
        if (!flag) {
            throw new ZekeException(400, "请传入规定合法账号类型");
        }
        if (type == UserConstants.DoctorUserType.PUB_NO_AUTH) {
            Integer isAuth = getBaseMapper().queryDoctorIsAuthApply(doctorId);
            if (isAuth != null) {
                throw new ZekeException(400, "账号已认证或提交认证无法关闭认证入口");
            }
        }
        DoctorUser update = new DoctorUser();
        update.setId(doctorId);
        update.setType(type);
        updateById(update);
    }

    /**
     * 医生账号注销
     *
     * @param request
     * @param phone
     * @param code
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void doctorUserAccountLogoff(HttpServletRequest request, String phone, String code) {
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo(LoginInfo.UserType.DOCTOR).get();
        LambdaQueryWrapper<DoctorDetail> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(DoctorDetail::getDoctorId, loginInfo.getId());
        DoctorDetail doctorDetail = doctorDetailService.getOne(wrapper);
        DoctorUser doctorUser = getById(loginInfo.getId());
        if (doctorUser == null) {
            throw new ZekeException(400, "账号不存在");
        }
        if (!doctorUser.getMobileNumber().equals(phone)) {
            throw new ZekeException(400, "请使用当前账号手机号验证");
        }
        smsComponent.verifyPhoneCode(phone, code);
        DoctorUser update = new DoctorUser();
        update.setId(doctorUser.getId());
        update.setStatus(UserConstants.DoctorUserStatus.LOGOFF);
        update.setLogoffDate(LocalDateTime.now());
        update.setLogoffName(doctorDetail.getName());
        updateById(update);
        doctorDetailService.delDoctorDetail(doctorUser.getId());
        String client = request.getHeader("client");
        redisTemplate.opsForValue().set(client + loginInfo.getType() + "-" + loginInfo.getId() + "-" + loginInfo.getType(), "loginOut");
        redisTemplate.expire(client + loginInfo.getType() + "-" + loginInfo.getId() + "-" + loginInfo.getType(), 60L, TimeUnit.MINUTES);
    }

    /**
     * 医生注销列表
     *
     * @param req
     * @return DoctorSysListRes
     */
    @Override
    public IPage<DoctorSysListRes> getLogoOffPage(DoctorSysListReq req) {
        IPage<DoctorSysListRes> iPage = req.pagePojo();
        if (StringUtils.isBlank(req.getStatus())) {
            req.setStatus(UserConstants.DoctorUserStatus.LOGO_OFF);
        }
        if (!StringUtils.isBlank(req.getStatus())) {
            req.setStatusStr(req.getStatus().split(","));
        }
        if (StringUtils.isNotBlank(req.getTypes())) {
            req.setTypesStr(req.getTypes().split(","));
        }
        iPage = getBaseMapper().getLogoOffPage(iPage, req);
        return iPage;
    }

    /**
     * 开启关闭处方权
     *
     * @param doctorId
     * @param status
     */
    @Override
    public void updatePresPower(Integer doctorId, Integer status) {
        DoctorUser doctorUser = this.getById(doctorId);
        LambdaQueryWrapper<DoctorDetail> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(DoctorDetail::getDoctorId, doctorId);
        DoctorDetail doctorDetail = doctorDetailService.getOne(wrapper);
        if (doctorUser == null) {
            throw new ZekeException("记录为空");
        }
        if (status == UserConstants.isPower.OPEN) {
            if (doctorUser.getIsCertification().equals(UserConstants.IsCertification.not_certification)) {
                throw new ZekeException("未认证咱不能开启处方权");
            }
            Integer qualificationType = doctorDetail.getQualificationType();
            if (qualificationType.equals(UserConstants.QualificationType.COUNTRY)) {
                doctorUser.setIsPresPower(UserConstants.isPower.OPEN);
                this.updateById(doctorUser);
            } else if (qualificationType.equals(UserConstants.QualificationType.PRACTICE)) {
                if (doctorUser.getIsCertification().equals(UserConstants.IsCertification.certification) && (StringUtils.isNotBlank(doctorDetail.getPraCertificatePhoto()) || StringUtils.isNotBlank(doctorDetail.getHealthCommitteePhoto()))) {
                    doctorUser.setIsPresPower(UserConstants.isPower.OPEN);
                    this.updateById(doctorUser);
                } else {
                    throw new ZekeException("暂不能开启处方权");
                }
            }
        } else if (status == UserConstants.isPower.NOT_OPEN) {
            doctorUser.setIsPresPower(UserConstants.isPower.NOT_OPEN);
            this.updateById(doctorUser);
        } else {
            throw new ZekeException("处方权标识错误");
        }
    }

    /**
     * 医生开通处方审方业务
     *
     * @param open
     */
    @Override
    public void openDoctorUserPresReviewBo(Integer open) {
        if (!ObjectUtils.refrect(UserConstants.IsOpenReviewBo.class, open)) {
            throw new ZekeException(400, "open参数值未定义");
        }
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo(LoginInfo.UserType.DOCTOR).get();
        DoctorUser doctorUser = getById(loginInfo.getId());
        if (doctorUser.getIsPresPower().equals(UserConstants.isPower.OPEN)) {
            DoctorUser update = new DoctorUser();
            update.setId(doctorUser.getId());
            update.setIsOpenReviewBo(open);
            update.setUpdateDate(LocalDateTime.now());
            updateById(update);
        } else {
            throw new ZekeException(400, "无法操作审方业务,未有处方权");
        }
    }

    /**
     * 查询可签约审方医生列表
     *
     * @param req
     * @return
     */
    @Override
    public IPage<CanSingingDoctorListRes> queryCanSingingDoctorList(CanSingingDoctorPageReq req) {
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo(LoginInfo.UserType.DOCTOR).get();
        IPage<CanSingingDoctorListRes> iPage = req.pagePojo();
        try {
            DoctorDetail dd = doctorDetailService.getOne(new QueryWrapper<DoctorDetail>().lambda().eq(DoctorDetail::getDoctorId, loginInfo.getId()));
            req.setProvince(dd.getProvince());
        } catch (Exception e) {
            e.printStackTrace();
        }
        iPage = getBaseMapper().queryCanSingingDoctorList(iPage, req);
        return iPage;
    }

    /**
     * 处理医生认证失效
     */
    @Override
    public void doctorAuthLapse() {
        LocalDate date1 = LocalDate.now().minusDays(30);
        LocalDate date2 = LocalDate.now().minusYears(5);
        List<Integer> ids1 = getBaseMapper().queryDoctorAuthLapseAuthType1IdList(date1);
        List<Integer> ids2 = getBaseMapper().queryDoctorAuthLapseAuthType2IdList(date2);
        if (!ids1.isEmpty()) {
            UpdateWrapper<DoctorUser> uw1 = new UpdateWrapper<>();
            uw1.lambda().in(DoctorUser::getId, ids1);
            uw1.lambda().set(DoctorUser::getIsCertification, UserConstants.IsCertification.not_certification);
            update(uw1);
        }
        if (!ids2.isEmpty()) {
            UpdateWrapper<DoctorUser> uw2 = new UpdateWrapper<>();
            uw2.lambda().in(DoctorUser::getId, ids2);
            uw2.lambda().set(DoctorUser::getIsCertification, UserConstants.IsCertification.not_certification);
            update(uw2);
        }
    }

    /**
     * 处理医生认证失效消息通知
     */
    @Override
    public void doctorAuthLapseNewsNotice() {
        LocalDate start1 = LocalDate.now().minusDays(30).plusDays(1);
        LocalDate end1 = LocalDate.now().minusDays(30).plusDays(6);
        LocalDate start2 = LocalDate.now().minusYears(5).plusDays(1);
        LocalDate end2 = LocalDate.now().minusYears(5).plusDays(31);
        List<Integer> ids1 = getBaseMapper().queryDoctorAuthLapseNewsNoticeAuthType1IdList(start1, end1);
        List<DoctorAuthLapseNewsNoticeAuthType2IdListRes> res2 = getBaseMapper().queryDoctorAuthLapseNewsNoticeAuthType2IdList(start2, end2);
        for (Integer item : ids1) {
            newsInfoService.sendDoctorAuthLapseNewsNoticeAuthType1(item);
        }
        for (DoctorAuthLapseNewsNoticeAuthType2IdListRes item : res2) {
            newsInfoService.sendDoctorAuthLapseNewsNoticeAuthType2(item.getDoctorId(), item.getDate());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void test() {
        //兼容邀请码
        List<DoctorRes> doctorRes = this.getBaseMapper().getInviteCode();
        List<DoctorUser> doctorUserList = this.getBaseMapper().selectList(null);
        Map<Integer, DoctorRes> mapCode = null;
        if (CollectionUtil.isNotEmpty(doctorUserList)) {
            if (CollectionUtil.isNotEmpty(doctorRes)) {
                mapCode = doctorRes.stream().collect(Collectors.toMap(DoctorRes::getDoctorId, Function.identity()));
            }
            for (DoctorUser doctorUser : doctorUserList) {
                if (doctorUser.getStatus() == UserConstants.DoctorUserStatus.TRIAL) {
                    doctorUser.setIsCertification(0);
                    doctorUser.setStatus(UserConstants.DoctorUserStatus.NORMAL);
                } else {
                    doctorUser.setIsCertification(1);
                }
                if (mapCode != null) {
                    DoctorRes res = mapCode.get(doctorUser.getId());
                    if (res != null) {
                        doctorUser.setCodeRecord(res.getInviteCode());
                        doctorUser.setInviteName(res.getInviteName());
                        doctorUser.setInviteType(res.getType());
                    }
                }
            }
            this.updateBatchById(doctorUserList);
        }
        List<DoctorDetail> doctorDetails = doctorDetailService.list();
        if (CollectionUtil.isNotEmpty(doctorDetails)) {
            for (DoctorDetail doctorDetail : doctorDetails) {
                doctorDetail.setQualificationType(2);
            }
            doctorDetailService.updateBatchById(doctorDetails);
        }
    }

    /**
     * 修改更新app版本赠送未认证使用免费次数版本号
     *
     * @param freeAiVersion
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateFreeAiVersion(String freeAiVersion) {
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo(LoginInfo.UserType.DOCTOR).get();
        DoctorUser doctorUser = getById(loginInfo.getId());
        if (doctorUser == null || doctorUser.getStatus() == UserConstants.DoctorUserStatus.DEL) {
            throw new ZekeException(400, "医生不存在");
        }
        DoctorUser update = new DoctorUser();
        update.setId(doctorUser.getId());
        update.setFreeAiVersion(freeAiVersion);
        updateById(update);
    }

    /**
     * 修改功能引导,科室引导
     *
     * @param functionGuide
     * @param depGuide
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateFunctionGuideOrDepGuide(String functionGuide, String depGuide) {
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo(LoginInfo.UserType.DOCTOR).get();
        DoctorUser doctorUser = getById(loginInfo.getId());
        if (doctorUser == null || doctorUser.getStatus() == UserConstants.DoctorUserStatus.DEL) {
            throw new ZekeException(400, "医生不存在");
        }
        DoctorUser update = new DoctorUser();
        update.setId(loginInfo.getId());
        if (StringUtils.isNotBlank(functionGuide)) {
            update.setFunctionGuide(functionGuide);
        }
        if (StringUtils.isNotBlank(depGuide)) {
            update.setDepGuide(depGuide);
        }
        if (StringUtils.isNotBlank(functionGuide) || StringUtils.isNotBlank(depGuide)) {
            updateById(update);
        }
    }

    @Override
    public void loginScanSaveTime(String timestamp) {
        DoctorQrScanReq req = new DoctorQrScanReq();
        req.setStatus(0);
        req.setTimestamp(timestamp);
        req.setDoctorId("");
        String json = JSON.toJSONString(req);

        redisTemplate.opsForValue().set(RedisKeyConstants.CacheKey.SCAN_LOGIN_DOCTOR + timestamp, json, 5, TimeUnit.MINUTES);
    }

    @Override
    public DoctorQrScanReq doctorScanIsLogin(String timestamp) {
        String result = redisTemplate.opsForValue().get(RedisKeyConstants.CacheKey.SCAN_LOGIN_DOCTOR + timestamp);
        if (StringUtils.isNotBlank(result)) {
            DoctorQrScanReq req = JSON.parseObject(result, DoctorQrScanReq.class);
            return req;
        } else {
            DoctorQrScanReq req = new DoctorQrScanReq();
            req.setStatus(-1);
            req.setTimestamp(timestamp);
            req.setDoctorId("");
            return req;
        }
    }

    @Override
    public void doctorScanQrClick(Integer status, String timestamp) {
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo(LoginInfo.UserType.DOCTOR).get();
        String result = redisTemplate.opsForValue().get(RedisKeyConstants.CacheKey.SCAN_LOGIN_DOCTOR + timestamp);
        if (StringUtils.isNotBlank(result)) {
            DoctorQrScanReq req = JSON.parseObject(result, DoctorQrScanReq.class);
            req.setStatus(status);
            req.setDoctorId(loginInfo.getId() + "");
            String json = JSON.toJSONString(req);
            long time = redisTemplate.getExpire(RedisKeyConstants.CacheKey.SCAN_LOGIN_DOCTOR + timestamp);
            redisTemplate.opsForValue().set(RedisKeyConstants.CacheKey.SCAN_LOGIN_DOCTOR + timestamp, json, time, TimeUnit.SECONDS);
        } else {
            throw new ZekeException("授权已失效,请重新扫码授权");
        }
    }

    @Override
    public DoctorUser doctorScanLogin(Integer doctorId, HttpServletRequest request, HttpServletResponse response, String timestamp) {
        //查询账号
        QueryWrapper<DoctorUser> q = new QueryWrapper<>();
        q.lambda().eq(DoctorUser::getId, doctorId);
        q.lambda().notIn(DoctorUser::getStatus, UserConstants.DoctorUserStatus.DEL, UserConstants.DoctorUserStatus.LOGOFF);
        DoctorUser doc = getOne(q);
        if (doc == null) {
            throw new ZekeException(ZkExceptionEnum.ACCOUNT_NOT_FOUND);
        }
        if (doc.getIsCertification() == UserConstants.IsCertification.certification) {
            MechanRank rank = mechanRankService.getById(doc.getMechanId());
            if (rank == null || rank.getStatus() == MechanConstants.MechanStatus.DEL) {
                throw new ZekeException(400, "机构不存在");
            }
            if (rank.getStatus() == MechanConstants.MechanStatus.DISABLEADMIN) {
                throw new ZekeException(400, "机构已被禁用");
            }
        }
        switchStatus(doc);
        QueryWrapper<DoctorDetail> qw = new QueryWrapper<>();
        qw.lambda().eq(DoctorDetail::getDoctorId, doc.getId()).eq(DoctorDetail::getStatus, UserConstants.DoctorDetailStatus.NORMAL);
        DoctorDetail doctorDetail = doctorDetailService.getOne(qw);
        if (doctorDetail == null) {
            throw new ZekeException(400, "账号无详情, 请联系管理员");
        }

        //生成token并写入cookie
        LoginInfo loginInfo = new LoginInfo(doc.getId(), doc.getMechanId(), LoginInfo.UserType.DOCTOR, doctorDetail.getName(), jwtUtils.getUserAgent(request));
        jwtUtils.createToken(loginInfo, request, response);
        List<ClientMenu> list = clientMenuUserService.getClientMenuUserList(loginInfo, request.getHeader("client"));
        doc.setMenuList(list);
        doc.setPassword("1");
        doc.setSalt(null);
        if (doc.getIsPresPower() == 0) {
            doc.setReviewPresDoctorCount(bindReviewPresDoctorService.queryBindReviewPresDoctorNormalCount(doc.getId()));
        }
        if (doc.getId() != null) {
            //登录发送积分
            String params = IntegralConstants.IntegralType.SIXTEEN_TYPE + "-" + doc.getId();
            log.info("连续登录,参数:{}", params);
            rabbitSend.sendMessage(MQConfig.INTEGRAL_EXCHANGE, MQConfig.INTEGRAL_QUEUE, params);
        }
        redisTemplate.delete(RedisKeyConstants.CacheKey.SCAN_LOGIN_DOCTOR + timestamp);
        return doc;
    }

    @Override
    public void deleteDoctorQr(String timestamp) {
        redisTemplate.delete(RedisKeyConstants.CacheKey.SCAN_LOGIN_DOCTOR + timestamp);
    }

    @Override
    public StatisticalUserNumRes getStatisticalUserNum() {
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo().get();
        return getBaseMapper().getStatisticalUserNum(loginInfo.getId());
    }

    @Override
    public List<DoctorStatisticalNodeRes> getStatisticalDateNode(DoctorStatisticalNodeReq req) {
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo(UserConstants.UserType.DOCTOR).get();
        Integer doctorId = loginInfo.getId();
        req.setDoctorId(doctorId);
        if (req.getCount() == null) {
            req.setCount(7);
        }
        LocalDateTime qu = LocalDateTime.now();
        switch (req.getNode()) {
            case "day":
                if (req.getEndDate() != null && req.getStartDate() != null) {
                    int day = (int) (req.getEndDate().toEpochDay() - req.getStartDate().toEpochDay());
                    req.setCount(day + 1);
                    req.setStartDate(req.getEndDate());
                } else {
                    LocalDate startDate = LocalDate.of(2023, 01, 01);
                    LocalDate endDate = LocalDate.now();
                    int day = (int) (endDate.toEpochDay() - startDate.toEpochDay());
                    req.setCount(day + 1);
                    req.setStartDate(endDate);
                }
                break;
            case "7day":
                req.setStartDate(LocalDate.now());
                req.setCount(7);
                break;
            case "30day":
                req.setStartDate(LocalDate.now());
                req.setCount(30);
                break;
            case "month":
                LocalDate localDate1 = LocalDate.of(2023, 01, 01);
                qu = DateUtils.getFirstDayOfMonth(LocalDate.now());
                req.setStartDate(qu.toLocalDate());
                int months = Period.between(localDate1, LocalDate.now()).getMonths() + 1;
                req.setCount(months);
                break;
            case "quarter":
                qu = DateUtils.quarterStartTime(LocalDate.now());
                req.setStartDate(qu.toLocalDate());
                req.setCount(4);
                break;
            case "year":
                //从今年开始计算
                LocalDate year = LocalDate.of(2023, 01, 01);
                qu = DateUtils.yearStartTime(null);
                req.setStartDate(qu.toLocalDate());
                int years = LocalDate.now().getYear() - year.getYear();
                req.setCount(years + 1);
                break;
            default:
                throw new ZekeException(400, "查询节点参数错误");
        }
        List<DoctorStatisticalNodeRes> list = getBaseMapper().getStatisticalDoctorDateNode(req);

        int size = list.size();
        for (int i = 0; i < size; i++) {
            DoctorStatisticalNodeRes res = list.get(i);
            res.setIncome(res.getNodeList().stream().mapToInt(DoctorStatisticalNodeRes::getIncome).sum());
            switch (req.getNode()) {
                case "day":
                case "7day":
                case "30day":
                    res.setDate(res.getDate().substring(5));
                    break;
                case "month":
                    String mon = res.getDate().substring(5, 7);
                    if (mon.equals("01") || i == 0) {
                        mon = res.getDate().substring(0, 4) + "年" + mon;
                    }
                    mon = mon + "月";
                    res.setDate(mon);
                    break;
                case "quarter":
                    if (res.getQu() == 1 || i == 0) {
                        res.setDate(res.getDate().substring(0, 4) + "第" + res.getQu() + "季度");
                    } else {
                        res.setDate("第" + res.getQu() + "季度");
                    }
                    break;
                default:
                    res.setDate(res.getDate().substring(0, 4));
                    break;
            }
        }
        return list;
    }

    @Override
    public List<DoctorStatisticalNodeRes> getStatisticalDoctorPresNumNode(DoctorStatisticalNodeReq req) {
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo(UserConstants.UserType.DOCTOR).get();
        Integer doctorId = loginInfo.getId();
        req.setDoctorId(doctorId);
        if (req.getCount() == null) {
            req.setCount(7);
        }
        LocalDateTime qu = LocalDateTime.now();
        switch (req.getNode()) {
            case "day":
                if (req.getEndDate() != null && req.getStartDate() != null) {
                    int day = (int) (req.getEndDate().toEpochDay() - req.getStartDate().toEpochDay());
                    req.setCount(day + 1);
                    req.setStartDate(req.getEndDate());
                } else {
                    LocalDate startDate = LocalDate.of(2023, 01, 01);
                    LocalDate endDate = LocalDate.now();
                    int day = (int) (endDate.toEpochDay() - startDate.toEpochDay());
                    req.setCount(day + 1);
                    req.setStartDate(endDate);
                }
                break;
            case "7day":
                req.setStartDate(LocalDate.now());
                req.setCount(7);
                break;
            case "30day":
                req.setStartDate(LocalDate.now());
                req.setCount(30);
                break;
            case "month":
                LocalDate localDate1 = LocalDate.of(2023, 01, 01);
                qu = DateUtils.getFirstDayOfMonth(LocalDate.now());
                req.setStartDate(qu.toLocalDate());
                int months = Period.between(localDate1, LocalDate.now()).getMonths() + 1;
                req.setCount(months);
                break;
            case "quarter":
                qu = DateUtils.quarterStartTime(LocalDate.now());
                req.setStartDate(qu.toLocalDate());
                req.setCount(4);
                break;
            case "year":
                //从今年开始计算
                LocalDate year = LocalDate.of(2023, 01, 01);
                qu = DateUtils.yearStartTime(null);
                req.setStartDate(qu.toLocalDate());
                int years = LocalDate.now().getYear() - year.getYear();
                req.setCount(years + 1);
                break;
            default:
                throw new ZekeException(400, "查询节点参数错误");
        }
        List<DoctorStatisticalNodeRes> list = getBaseMapper().getStatisticalDoctorPresNumNode(req);

        int size = list.size();
        for (int i = 0; i < size; i++) {
            DoctorStatisticalNodeRes res = list.get(i);
            res.setPreNums(res.getNodeList().stream().mapToInt(DoctorStatisticalNodeRes::getPreNums).sum());
            switch (req.getNode()) {
                case "day":
                case "7day":
                case "30day":
                    res.setDate(res.getDate().substring(5));
                    break;
                case "month":
                    String mon = res.getDate().substring(5, 7);
                    if (mon.equals("01") || i == 0) {
                        mon = res.getDate().substring(0, 4) + "年" + mon;
                    }
                    mon = mon + "月";
                    res.setDate(mon);
                    break;
                case "quarter":
                    if (res.getQu() == 1 || i == 0) {
                        res.setDate(res.getDate().substring(0, 4) + "第" + res.getQu() + "季度");
                    } else {
                        res.setDate("第" + res.getQu() + "季度");
                    }
                    break;
                default:
                    res.setDate(res.getDate().substring(0, 4));
                    break;
            }
        }
        return list;
    }

    @Override
    public PageVo<DoctorIntegral> queryDoctorIntegralPage(DoctorIntegralPageReq req) {
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo(UserConstants.UserType.DOCTOR).get();
        Integer doctorId = loginInfo.getId();
        req.setDoctorId(doctorId);
        if (StringUtils.isBlank(req.getStatus())) {
            req.setStatus("-2,2");
        }
        PageVo<DoctorIntegral> page = req.pageVoPojo();
        page = this.getBaseMapper().queryDoctorIntegralPage(page, req);
        DoctorIntegralDataRes dataRes = new DoctorIntegralDataRes();
        dataRes.setGive(0);
        dataRes.setUse(0);
        List<DoctorIntegral> doctorIntegralList = this.getBaseMapper().queryDoctorIntegralList(req);
        if (CollectionUtil.isNotEmpty(doctorIntegralList)) {
            //获取获取到的积分
            if (CollectionUtil.isNotEmpty(doctorIntegralList)) {
                Integer received = selectDoctorIntegral(IntegralConstants.status.RECEIVED, doctorIntegralList);
                Integer use = selectDoctorIntegral(IntegralConstants.status.RETURN, doctorIntegralList);
                dataRes.setUse(use);
                dataRes.setGive(received);
            }
        }
        page.setExtend(dataRes);
        return page;
    }


    private Integer selectDoctorIntegral(Integer status, List<DoctorIntegral> doctorIntegralList) {
        List<DoctorIntegral> list = doctorIntegralList.stream().filter(item -> status.equals(item.getStatus())).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(list)) {
            return list.stream().mapToInt(DoctorIntegral::getNum).sum();
        } else {
            return 0;
        }
    }
}
